This file is used to prepare the figures for the paper.

library(dplyr)
library(patchwork)
library(ggplot2)
library(ComplexHeatmap)
library(org.Mm.eg.db)

.libPaths()
## [1] "/usr/local/lib/R/library"

Preparation

Here are the folders where analyzes are stored :

data_dir = "./.."
list.files(data_dir)
##  [1] "0_intro"      "1_metadata"   "2_individual" "3_combined"   "4_zoom"      
##  [6] "5_wu"         "6_figures"    "LICENSE"      "index.html"   "index_layout"

We load the dataset containing all cells :

sobj = readRDS(paste0(data_dir, "/3_combined/hs_hd_sobj.rds"))
sobj
## An object of class Seurat 
## 20003 features across 12111 samples within 1 assay 
## Active assay: RNA (20003 features, 2000 variable features)
##  6 dimensional reductions calculated: RNA_pca, RNA_pca_38_tsne, RNA_pca_38_umap, harmony, harmony_38_umap, harmony_38_tsne

These are all the samples analyzed :

sample_info = readRDS(paste0(data_dir, "/1_metadata/hs_hd_sample_info.rds"))

# Nb cells by dataset
to_plot = table(sobj$sample_identifier) %>%
  as.data.frame.table(., stringsAsFactors = FALSE) %>%
  `colnames<-`(c("sample_identifier", "nb_cells")) %>%
  dplyr::left_join(x = ., y = sample_info, by = "sample_identifier") 

# patchwork
plot_list = aquarius::fig_plot_gb(to_plot, title = "Available datasets")
patchwork::wrap_plots(plot_list) +
  patchwork::plot_layout(design = "A\nB", heights = c(0.1,5)) &
  ggplot2::theme(plot.title = element_text(hjust = 0.5, face = "bold", size = 15))

These are the custom colors for cell populations :

color_markers = readRDS(paste0(data_dir, "/1_metadata/hs_hd_color_markers.rds"))
color_markers = color_markers[names(color_markers) != "melanocytes"]
ors_color = color_markers["ORS"]
color_markers["ORS"] = color_markers["IFE"] 
color_markers["IFE"] = ors_color
color_markers["B cells"] = "chocolate3"
rm(ors_color)

# re-order
color_markers = color_markers[c("CD4 T cells", "CD8 T cells", "Langerhans cells", "macrophages", "B cells",
                                "cuticle", "cortex", "medulla", "IRS", "proliferative",
                                "HFSC", "ORS", "IBL", "IFE", "sebocytes")]

data.frame(cell_type = names(color_markers),
           color = unlist(color_markers)) %>%
  ggplot2::ggplot(., aes(x = cell_type, y = 0, fill = cell_type)) +
  ggplot2::geom_point(pch = 21, size = 5) +
  ggplot2::scale_fill_manual(values = unlist(color_markers), breaks = names(color_markers)) +
  ggplot2::theme_classic() +
  ggplot2::theme(legend.position = "none",
                 axis.line = element_blank(),
                 axis.title = element_blank(),
                 axis.ticks = element_blank(),
                 axis.text.y = element_blank(),
                 axis.text.x = element_text(hjust = 1, angle = 20))

We define custom colors for sample type :

sample_type_colors = setNames(nm = levels(sample_info$sample_type),
                              c("#C55F40", "#2C78E6"))

data.frame(cell_type = names(sample_type_colors),
           color = unlist(sample_type_colors)) %>%
  ggplot2::ggplot(., aes(x = cell_type, y = 0, fill = cell_type)) +
  ggplot2::geom_point(pch = 21, size = 5) +
  ggplot2::scale_fill_manual(values = unlist(sample_type_colors), breaks = names(sample_type_colors)) +
  ggplot2::theme_classic() +
  ggplot2::theme(legend.position = "none",
                 axis.line = element_blank(),
                 axis.title = element_blank(),
                 axis.ticks = element_blank(),
                 axis.text.y = element_blank())

We set a background color :

bg_color = "gray94"

This is the correspondence between cell types and cell families, and custom colors to color cells by cell family :

custom_order_cell_type = data.frame(
  cell_type = names(color_markers),
  cell_family = c(rep("immune cells", 5),
                  rep("matrix", 5),
                  rep("non matrix", 5)),
  stringsAsFactors = FALSE)
custom_order_cell_type$cell_type = factor(custom_order_cell_type$cell_type,
                                          levels = custom_order_cell_type$cell_type)
rownames(custom_order_cell_type) = custom_order_cell_type$cell_type

family_color = c("immune cells" = "slateblue1",
                 "matrix" = "mediumseagreen",
                 "non matrix" = "firebrick3")

We load markers to display on a dotplot to assess cell type annotation :

dotplot_markers = readRDS(paste0(data_dir, "/1_metadata/hs_hd_dotplot_markers.rds"))
dotplot_markers = dotplot_markers[names(dotplot_markers) != "melanocytes"]
lengths(dotplot_markers)
##      CD4 T cells      CD8 T cells Langerhans cells      macrophages 
##                2                2                2                2 
##          B cells          cuticle           cortex          medulla 
##                2                2                2                2 
##              IRS    proliferative              IBL              ORS 
##                2                2                2                2 
##              IFE             HFSC        sebocytes 
##                2                2                2

Custom functions to display gene expression on the heatmap :

color_fun = function(one_gene) {
  gene_range = range(ht_annot[, one_gene])
  gene_palette = circlize::colorRamp2(colors = c("#FFFFFF", aquarius::color_gene[-1]),
                                      breaks = seq(from = gene_range[1], to = gene_range[2],
                                                   length.out = length(aquarius::color_gene)))
  return(gene_palette)
}

All samples

Settings

This is the projection name to visualize cells :

name2D = "harmony_38_tsne"
name2D_atlas = name2D

Preparation

We make a low resolutive clustering for the heatmap :

sobj = Seurat::FindClusters(sobj, resolution = 0.4)
## Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck
## 
## Number of nodes: 12111
## Number of edges: 475472
## 
## Running Louvain algorithm...
## Maximum modularity in 10 random starts: 0.9421
## Number of communities: 17
## Elapsed time: 1 seconds
length(levels(sobj$seurat_clusters))
## [1] 17

We define cluster type and cluster family :

sobj$cell_type = sobj$cell_type %>%
  as.character() %>%
  factor(., levels = names(color_markers))

cluster_type = table(sobj$cell_type, sobj$seurat_clusters) %>%
  prop.table(., margin = 2) %>%
  apply(., 2, which.max)
cluster_type = setNames(nm = names(cluster_type),
                        levels(sobj$cell_type)[cluster_type])

sobj$cluster_type = cluster_type[sobj$seurat_clusters]
sobj$cluster_type = factor(sobj$cluster_type,
                           levels = levels(sobj$cell_type))
sobj$cluster_family = custom_order_cell_type[sobj$cluster_type, "cell_family"]
sobj$cluster_family = factor(sobj$cluster_family,
                             levels = names(family_color))

Figures

Project name :

# Random order
set.seed(1234)
rnd_order = sample(colnames(sobj), replace = FALSE, size = ncol(sobj))

# Extract coordinates
cells_coord = sobj@reductions[[name2D]]@cell.embeddings %>%
  as.data.frame() %>%
  `colnames<-`(c("Dim1", "Dim2"))
cells_coord$project_name = sobj$project_name
cells_coord = cells_coord[(rnd_order), ]

# Plot
ggplot2::ggplot(cells_coord, aes(x = Dim1, y = Dim2, col = project_name)) +
  ggplot2::geom_point(size = 0.5) +
  ggplot2::scale_color_manual(values = sample_info$color,
                              breaks = sample_info$project_name) +
  ggplot2::theme_void() +
  ggplot2::theme(aspect.ratio = 1,
                 legend.position = "none")

Sample type :

# Extract coordinates
cells_coord = sobj@reductions[[name2D]]@cell.embeddings %>%
  as.data.frame() %>%
  `colnames<-`(c("Dim1", "Dim2"))
cells_coord$sample_type = sobj$sample_type
cells_coord = cells_coord[(rnd_order), ]

# Plot
ggplot2::ggplot(cells_coord, aes(x = Dim1, y = Dim2, col = sample_type)) +
  ggplot2::geom_point(size = 0.5) +
  ggplot2::scale_color_manual(values = sample_type_colors,
                              breaks = names(sample_type_colors)) +
  ggplot2::theme_void() +
  ggplot2::theme(aspect.ratio = 1,
                 legend.position = "none")

Cluster :

grey_palette = setNames(nm = levels(sobj$seurat_clusters),
                        rep("#D9D9D9", length(levels(sobj$seurat_clusters))))
grey_palette[c("7", "16", "1", "12", "11", "10", "15")] = "#BDBDBD"
grey_palette[c("16", "14", "5", "9")] = "#969696"

Seurat::DimPlot(sobj, reduction = name2D, pt.size = 0.4,
                group.by = "seurat_clusters", cols = grey_palette,
                label = TRUE, label.size = 6) +
  ggplot2::theme(aspect.ratio = 1) +
  Seurat::NoAxes() +
  Seurat::NoLegend()

Cell type annotation :

Seurat::DimPlot(sobj, reduction = name2D, pt.size = 0.5,
                group.by = "cell_type", cols = color_markers) +
  ggplot2::theme(aspect.ratio = 1) +
  Seurat::NoAxes() +
  Seurat::NoLegend()

Cluster family annotation :

Seurat::DimPlot(sobj, reduction = name2D, pt.size = 0.5,
                group.by = "cluster_family", cols = family_color) +
  ggplot2::theme(aspect.ratio = 1) +
  Seurat::NoAxes() +
  Seurat::NoLegend()

Cell type annotation split by condition :

plot_list = aquarius::plot_split_dimred(sobj, reduction = name2D,
                                        group_by = "cell_type",
                                        group_color = color_markers,
                                        split_by = "sample_type",
                                        split_color = sample_type_colors,
                                        bg_color = bg_color)

patchwork::wrap_plots(plot_list) &
  Seurat::NoLegend()

Gene expression to assess annotation :

genes = c("PTPRC", "MSX2", "KRT14")
names(genes) = c("immune cells", "matrix cells", "non-matrix cells")

plot_list = lapply(c(1:length(genes)), FUN = function(gene_id) {
  gene = genes[[gene_id]]
  pop = names(genes)[gene_id]
  
  sobj$my_gene = Seurat::FetchData(sobj, gene)[, 1] %>%
    aquarius::run_rescale(., new_min = 0, new_max = 10)
  
  Seurat::FeaturePlot(sobj, features = "my_gene", reduction = name2D) +
    ggplot2::scale_color_gradientn(colors = aquarius::color_gene,
                                   breaks = seq(0, 10, by = 2.5),
                                   labels = c("min", rep("", 3), "max")) +
    ggplot2::labs(title = gene) + 
    # subtitle = pop) +
    ggplot2::theme(aspect.ratio = 1,
                   plot.title = element_text(hjust = 0.5, size = 17),
                   plot.subtitle = element_text(hjust = 0.5, size = 15),
                   legend.text = element_text(size = 15),
                   legend.position = "none") +
    Seurat::NoAxes()
})

plot_list
## [[1]]

## 
## [[2]]

## 
## [[3]]

Barplot by cluster family :

quantif = table(sobj$sample_identifier) %>%
  as.data.frame.table() %>%
  `colnames<-`(c("Sample", "nb_cells"))

aquarius::plot_barplot(df = table(sobj$sample_identifier,
                                  sobj$cluster_family) %>%
                         as.data.frame.table() %>%
                         `colnames<-`(c("Sample", "Cell Type", "Number")),
                       x = "Sample", y = "Number", fill = "Cell Type",
                       position = position_fill()) +
  ggplot2::geom_label(data = quantif, inherit.aes = FALSE,
                      aes(x = .data$Sample, y = 1.05, label = .data$nb_cells),
                      label.size = 0, size = 4) +
  ggplot2::scale_fill_manual(values = unlist(family_color),
                             breaks = names(family_color),
                             name = "Cell Family") +
  ggplot2::scale_y_continuous(breaks = seq(0, 1, by = 0.25),
                              labels = paste0(seq(0, 100, by = 25), sep = " %"),
                              expand = ggplot2::expansion(add = c(0, 0.05))) +
  ggplot2::theme(axis.title.y = element_blank(),
                 axis.line.x = element_line(colour = "lightgray"),
                 text = element_text(size = 15),
                 axis.text.x = element_text(margin = margin(t = 25, r = 0, b = 0, l = 0)),
                 legend.position = "none")

Heatmap of cluster proportion by sample :

group_by = "seurat_clusters"

cluster_by_sample = table(sobj$sample_identifier,
                          sobj@meta.data[, group_by]) %>%
  prop.table(margin = 1) %>%
  as.matrix()

## Right annotation : number of cells by dataset
ht_annot = table(sobj$sample_identifier) %>%
  as.data.frame.table() %>%
  `colnames<-`(c("sample_identifier", "nb_cells")) %>%
  `rownames<-`(.$sample_identifier) %>%
  dplyr::select(-sample_identifier)

ha_right = ComplexHeatmap::HeatmapAnnotation(
  df = ht_annot,
  which = "row",
  show_legend = TRUE,
  annotation_name_side = "top",
  col = list(nb_cells  = circlize::colorRamp2(colors = RColorBrewer::brewer.pal(name = "Greys", n = 9),
                                              breaks = seq(from = range(ht_annot$nb_cells)[1],
                                                           to = range(ht_annot$nb_cells)[2],
                                                           length.out = 9))))

## Left annotation : gender
ha_left = ComplexHeatmap::HeatmapAnnotation(
  gender = sample_info$gender,
  which = "row",
  show_legend = TRUE,
  annotation_name_side = "top",
  col = list(gender = setNames(nm = c("F", "M"),
                               c("lightcyan3", "navyblue"))))

## Top annotation : main cell type in this cluster
ht_annot = table(sobj$sample_identifier,
                 sobj@meta.data[, group_by]) %>%
  prop.table(margin = 1) %>%
  as.matrix()

ht_annot = table(sobj$cell_type,
                 sobj@meta.data[, group_by]) %>%
  prop.table(., margin = 2) %>%
  apply(., 2, which.max)
ht_annot = data.frame(row.names = names(ht_annot),
                      cell_type = names(color_markers)[ht_annot],
                      stringsAsFactors = FALSE)
ht_annot = dplyr::left_join(ht_annot, custom_order_cell_type, by = "cell_type") %>%
  # Simplification for matrix
  dplyr::mutate(cell_type = ifelse(cell_type %in% c("medulla", "cortex", "cuticle"), yes = "hair shaft", no = cell_type)) %>%
  # Simplification for T cells
  dplyr::mutate(cell_type = ifelse(cell_type %in% c("CD4 T cells", "CD8 T cells"), yes = "T cells", no = cell_type)) %>%
  # Simplification for APC
  dplyr::mutate(cell_type = ifelse(cell_type %in% c("Langerhans cells", "macrophages"), yes = "APC", no = cell_type)) %>%
  # Add color
  dplyr::mutate(color = as.character(color_markers[cell_type])) %>%
  dplyr::mutate(color = ifelse(cell_type == "hair shaft", yes = "#FFB6C1", no = color)) %>%
  dplyr::mutate(color = ifelse(cell_type == "T cells", yes = "#8A6EE6", no = color)) %>%
  dplyr::mutate(color = ifelse(cell_type == "APC", yes = "#9CAA4B", no = color))

ha_top = ComplexHeatmap::HeatmapAnnotation(
  # cell_type = ht_annot$cell_type,
  cell_family = ht_annot$cell_family,
  which = "column",
  show_legend = TRUE,
  show_annotation_name = FALSE,
  # annotation_name_side = "left",
  col = list(#cell_type = setNames(nm = ht_annot$cell_type,
    #                      ht_annot$color),
    cell_family = family_color
  ))

## Assemble heatmap
ht = ComplexHeatmap::Heatmap(cluster_by_sample,
                             heatmap_legend_param = list(title = "Proportion",
                                                         col = c("#2166AC", "#F7F7F7", "#B2182B")),
                             # bottom_annotation = ha_bottom,
                             right_annotation = ha_right,
                             left_annotation = ha_left,
                             top_annotation = ha_top,
                             cluster_rows = TRUE,
                             cluster_columns = TRUE,
                             row_title = "Sample",
                             row_names_gp = grid::gpar(names = sample_info$sample_identifier,
                                                       col = sample_info$color,
                                                       fontface = "bold"),
                             column_title = "Cluster",
                             column_names_centered = TRUE,
                             row_names_side = "left",
                             column_names_side = "top",
                             column_names_rot = 0)

## Draw !
ComplexHeatmap::draw(ht, merge_legends = TRUE)

For the dotplot, we clarify clusters and cell type annotation :

cell_type_in_cluster = table(sobj$cell_type, sobj$seurat_clusters) %>%
  prop.table(., margin = 1) %>%
  apply(., 1, which.max)
cell_type_in_cluster = cell_type_in_cluster - 1

missing_cluster = setdiff(levels(sobj$seurat_clusters),
                          cell_type_in_cluster)

cell_type_in_cluster = data.frame(cell_type = c(names(cell_type_in_cluster), cluster_type[missing_cluster]),
                                  cluster_id = c(cell_type_in_cluster, names(cluster_type[missing_cluster])),
                                  stringsAsFactors = FALSE, row.names = NULL) %>%
  dplyr::mutate(cluster_id = as.numeric(cluster_id)) %>%
  dplyr::arrange(cell_type, cluster_id)

custom_order_cell_type$clusters = custom_order_cell_type %>%
  apply(., MARGIN = 1, FUN = function(one_row) {
    cell_type = one_row["cell_type"]
    clusters = cell_type_in_cluster %>%
      dplyr::filter(.data$cell_type == .env$cell_type) %>%
      dplyr::pull(cluster_id)
    
    cell_type_cluster = paste0(cell_type, " (", paste0(clusters, collapse = ", "), ")")
    
    return(cell_type_cluster)
  }) %>%
  factor(., levels = .)

custom_order_cell_type
##                         cell_type  cell_family                     clusters
## CD4 T cells           CD4 T cells immune cells              CD4 T cells (2)
## CD8 T cells           CD8 T cells immune cells          CD8 T cells (2, 14)
## Langerhans cells Langerhans cells immune cells         Langerhans cells (6)
## macrophages           macrophages immune cells              macrophages (6)
## B cells                   B cells immune cells                 B cells (16)
## cuticle                   cuticle       matrix                  cuticle (3)
## cortex                     cortex       matrix                   cortex (3)
## medulla                   medulla       matrix                 medulla (11)
## IRS                           IRS       matrix                     IRS (15)
## proliferative       proliferative       matrix proliferative (4, 9, 10, 13)
## HFSC                         HFSC   non matrix                  HFSC (7, 8)
## ORS                           ORS   non matrix                      ORS (0)
## IBL                           IBL   non matrix                      IBL (1)
## IFE                           IFE   non matrix                      IFE (5)
## sebocytes               sebocytes   non matrix               sebocytes (12)

Dotplot :

plot_list = aquarius::plot_dotplot(sobj,
                                   markers = c("PTPRC",
                                               "CD3E", "CD4",
                                               "CD3E", "CD8A",
                                               "CD207", "AIF1",
                                               "TREM2", "MSR1",
                                               "CD79A", "CD79B",
                                               # "PRDM1", "KRT85",
                                               "MSX2",
                                               "KRT32", "KRT35",
                                               "KRT31", "PRR9",
                                               "BAMBI", "ALDH1A3",
                                               "KRT71", "KRT73",
                                               "TOP2A", "MCM5",
                                               "KRT14", "CXCL14",
                                               "KRT15", "COL17A1",
                                               "DIO2", "TCEAL2",
                                               "KRT16", "KRT6C",
                                               "SPINK5", "LY6D",
                                               "CLMP", "PPARG"),
                                   assay = "RNA", column_name = "cell_type", nb_hline = 0) +
  ggplot2::scale_color_gradientn(colors = aquarius::color_gene) +
  ggplot2::theme(legend.position = "left",
                 legend.justification = "bottom",
                 legend.box = "vertical",
                 legend.box.margin = margin(0,70,0,0),
                 axis.title = element_blank(),
                 axis.ticks.x = element_blank(),
                 axis.text.x = element_blank(),
                 axis.line.x = element_blank(),
                 plot.margin = unit(rep(0, 4), "cm"))

p = ggplot2::ggplot(custom_order_cell_type, aes(x = clusters, y = 0)) +
  ggplot2::geom_point(size = 0) +
  ggplot2::geom_segment(aes(x = 0.5, xend = 5.5, y = 0, yend = 0), size = 6, col = family_color["immune cells"]) +
  ggplot2::geom_segment(aes(x = 5.5, xend = 10.5, y = 0, yend = 0), size = 6, col = family_color["matrix"]) +
  ggplot2::geom_segment(aes(x = 10.5, xend = 15.5, y = 0, yend = 0), size = 6, col = family_color["non matrix"]) +
  ggplot2::scale_y_continuous(expand = c(0,0), limits = c(0,0)) +
  ggplot2::theme_classic() +
  ggplot2::theme(axis.text.y = element_blank(),
                 axis.ticks.y = element_blank(),
                 axis.title = element_blank(),
                 axis.line.y = element_blank(),
                 axis.text.x = element_text(angle = 45, hjust = 1, size = 10, color = "black"),
                 plot.margin = unit(c(0,0.5,0.5,0), "cm"))

plot_list = patchwork::wrap_plots(plot_list, p,
                                  ncol = 1, heights = c(25, 1))
plot_list

Immune cells

Settings

We load the immune cells dataset :

sobj_ic = readRDS(paste0(data_dir, "/4_zoom/1_zoom_immune/immune_cells_sobj.rds"))
sobj_ic
## An object of class Seurat 
## 15121 features across 2329 samples within 1 assay 
## Active assay: RNA (15121 features, 2000 variable features)
##  6 dimensional reductions calculated: RNA_pca, RNA_pca_20_tsne, RNA_pca_20_umap, harmony, harmony_20_umap, harmony_20_tsne

This is the projection name to visualize cells :

name2D = "harmony_20_tsne"

To represent results from differential expression, we load the analyses results :

list_results = readRDS(paste0(data_dir, "/4_zoom/1_zoom_immune/immune_cells_list_results.rds"))

lapply(list_results, FUN = names)
## $`Langerhans cells`
## [1] "mark" "gsea"
## 
## $macrophages
## [1] "mark"       "enrichr_hs" "enrichr_hd" "gsea"      
## 
## $`CD4 T cells`
## [1] "mark"       "enrichr_hs" "enrichr_hd" "gsea"      
## 
## $`CD8 T cells`
## [1] "mark"       "enrichr_hs" "enrichr_hd" "gsea"

Preparation

We defined cluster type and cluster family :

cluster_type = table(sobj_ic$cell_type, sobj_ic$seurat_clusters) %>%
  prop.table(., margin = 2) %>%
  apply(., 2, which.max)
cluster_type = setNames(nm = names(cluster_type),
                        levels(sobj_ic$cell_type)[cluster_type])

sobj_ic$cluster_type = cluster_type[sobj_ic$seurat_clusters]
sobj_ic$cluster_type = factor(sobj_ic$cluster_type,
                              levels = levels(sobj_ic$cell_type))
sobj_ic$cluster_family = custom_order_cell_type[sobj_ic$cluster_type, "cell_family"]
sobj_ic$cluster_family = factor(sobj_ic$cluster_family,
                                levels = names(family_color))

Figures

Control cells on the full atlas :

sobj$is_immune = (colnames(sobj) %in% colnames(sobj_ic))

Seurat::DimPlot(sobj, reduction = name2D_atlas, pt.size = 0.000001,
                group.by = "is_immune", order = "TRUE") +
  ggplot2::scale_color_manual(values = c(family_color[["immune cells"]], bg_color),
                              breaks = c(TRUE, FALSE)) +
  ggplot2::labs(title = "Immune cells",
                subtitle = paste0(ncol(sobj_ic), " cells")) +
  ggplot2::theme(aspect.ratio = 1,
                 plot.title = element_text(hjust = 0.5, face = "bold"),
                 plot.subtitle = element_text(hjust = 0.5)) +
  Seurat::NoAxes() + Seurat::NoLegend()

Violin plot of IL1B in macrophages :

subsobj = subset(sobj_ic, seurat_clusters == 2)
table(subsobj$sample_type)
## 
##  HS  HD 
## 378  31
il1b_hs = subsobj@assays$RNA@data["IL1B", subsobj$sample_type == "HS"]
il1b_hd = subsobj@assays$RNA@data["IL1B", subsobj$sample_type == "HD"]
il1b_hs_VS_il1b_hd = stats::t.test(il1b_hs, il1b_hd)
il1b_hs_VS_il1b_hd
## 
##  Welch Two Sample t-test
## 
## data:  il1b_hs and il1b_hd
## t = 2.3206, df = 35.801, p-value = 0.02612
## alternative hypothesis: true difference in means is not equal to 0
## 95 percent confidence interval:
##  0.05066074 0.75429252
## sample estimates:
## mean of x mean of y 
## 0.9256690 0.5231924
Seurat::VlnPlot(subsobj, group.by = "sample_type", pt.size = 0.3,
                features = "IL1B", cols = sample_type_colors) +
  ggplot2::theme(axis.title.x = element_blank(),
                 legend.position = "none")

Split by sample :

Seurat::VlnPlot(subsobj, group.by = "sample_identifier",
                features = "IL1B", cols = sample_info$color) +
  ggplot2::theme(axis.title.x = element_blank(),
                 legend.position = "none")

Violin plot of IL1B in macrophages :

il6_hs = subsobj@assays$RNA@data["IL6", subsobj$sample_type == "HS"]
il6_hd = subsobj@assays$RNA@data["IL6", subsobj$sample_type == "HD"]
il6_hs_VS_il6_hd = stats::t.test(il6_hs, il6_hd)
il6_hs_VS_il6_hd
## 
##  Welch Two Sample t-test
## 
## data:  il6_hs and il6_hd
## t = 2.3591, df = 377, p-value = 0.01883
## alternative hypothesis: true difference in means is not equal to 0
## 95 percent confidence interval:
##  0.001453521 0.016004661
## sample estimates:
##   mean of x   mean of y 
## 0.008729091 0.000000000
Seurat::VlnPlot(subsobj, group.by = "sample_type", pt.size = 0.3,
                features = "IL6", cols = sample_type_colors) +
  ggplot2::theme(axis.title.x = element_blank(),
                 legend.position = "none")

Violin plot of TNF in macrophages :

tnf_hs = subsobj@assays$RNA@data["TNF", subsobj$sample_type == "HS"]
tnf_hd = subsobj@assays$RNA@data["TNF", subsobj$sample_type == "HD"]
tnf_hs_VS_tnf_hd = stats::t.test(tnf_hs, tnf_hd)
tnf_hs_VS_tnf_hd
## 
##  Welch Two Sample t-test
## 
## data:  tnf_hs and tnf_hd
## t = 3.8395, df = 45.546, p-value = 0.0003786
## alternative hypothesis: true difference in means is not equal to 0
## 95 percent confidence interval:
##  0.1140962 0.3657054
## sample estimates:
##  mean of x  mean of y 
## 0.31784960 0.07794879
Seurat::VlnPlot(subsobj, group.by = "sample_type", pt.size = 0.3,
                features = "TNF", cols = sample_type_colors) +
  ggplot2::theme(axis.title.x = element_blank(),
                 legend.position = "none")

Split by sample :

Seurat::VlnPlot(subsobj, group.by = "sample_identifier",
                features = "TNF", cols = sample_info$color) +
  ggplot2::theme(axis.title.x = element_blank(),
                 legend.position = "none")

Violin plot of GZMA in CD4 T cells :

subsobj = subset(sobj_ic, seurat_clusters %in% c(0,10))
table(subsobj$sample_type)
## 
##  HS  HD 
## 569  90
gzma_hs = subsobj@assays$RNA@data["GZMA", subsobj$sample_type == "HS"]
gzma_hd = subsobj@assays$RNA@data["GZMA", subsobj$sample_type == "HD"]
gzma_hs_VS_gzma_hd = stats::t.test(gzma_hs, gzma_hd)
gzma_hs_VS_gzma_hd
## 
##  Welch Two Sample t-test
## 
## data:  gzma_hs and gzma_hd
## t = 14.755, df = 172.51, p-value < 2.2e-16
## alternative hypothesis: true difference in means is not equal to 0
## 95 percent confidence interval:
##  1.225578 1.604097
## sample estimates:
## mean of x mean of y 
## 1.8456108 0.4307735
Seurat::VlnPlot(subsobj, group.by = "sample_type", pt.size = 0.3,
                features = "GZMA", cols = sample_type_colors) +
  ggplot2::theme(axis.title.x = element_blank(),
                 legend.position = "none")

Violin plot of IFNG in CD4 T cells :

ifng_hs = subsobj@assays$RNA@data["IFNG", subsobj$sample_type == "HS"]
ifng_hd = subsobj@assays$RNA@data["IFNG", subsobj$sample_type == "HD"]
ifng_hs_VS_ifng_hd = stats::t.test(ifng_hs, ifng_hd)
ifng_hs_VS_ifng_hd
## 
##  Welch Two Sample t-test
## 
## data:  ifng_hs and ifng_hd
## t = 8.1978, df = 651.25, p-value = 1.303e-15
## alternative hypothesis: true difference in means is not equal to 0
## 95 percent confidence interval:
##  0.1874856 0.3055919
## sample estimates:
##  mean of x  mean of y 
## 0.25885178 0.01231299
Seurat::VlnPlot(subsobj, group.by = "sample_type", pt.size = 0.3,
                features = "IFNG", cols = sample_type_colors) +
  ggplot2::theme(axis.title.x = element_blank(),
                 legend.position = "none")

Violin plot of IL17A in CD4 T cells :

IL17_hs = subsobj@assays$RNA@data["IL17A", subsobj$sample_type == "HS"]
IL17_hd = subsobj@assays$RNA@data["IL17A", subsobj$sample_type == "HD"]
IL17_hs_VS_IL17_hd = stats::t.test(IL17_hs, IL17_hd)
IL17_hs_VS_IL17_hd
## 
##  Welch Two Sample t-test
## 
## data:  IL17_hs and IL17_hd
## t = 4.5667, df = 219.64, p-value = 8.255e-06
## alternative hypothesis: true difference in means is not equal to 0
## 95 percent confidence interval:
##  0.1412906 0.3558327
## sample estimates:
##  mean of x  mean of y 
## 0.30323619 0.05467451
Seurat::VlnPlot(subsobj, group.by = "sample_type", pt.size = 0.3,
                features = "IL17RE", cols = sample_type_colors) +
  ggplot2::theme(axis.title.x = element_blank(),
                 legend.position = "none")

We represent some genes split by sample type :

plot_list = lapply(c("IL1B", "GZMA", "IFNG", "IL17A", "TNF", "IL6"), FUN = function(one_gene) {
  p = aquarius::plot_split_dimred(sobj_ic,
                                  reduction = name2D,
                                  split_by = "sample_type",
                                  color_by = one_gene,
                                  color_palette = c("gray70", "#FDBB84", "#EF6548", "#7F0000", "black"),
                                  main_pt_size = 0.6,
                                  bg_pt_size = 0.6,
                                  order = TRUE,
                                  bg_color = "gray95")
  p = patchwork::wrap_plots(p, nrow = 1) +
    patchwork::plot_layout(guides = "collect") +
    ggplot2::theme(legend.position = "right") &
    ggplot2::theme(plot.subtitle = element_blank())
  return(p)
})

plot_list
## [[1]]

## 
## [[2]]

## 
## [[3]]

## 
## [[4]]

## 
## [[5]]

## 
## [[6]]

Barplot by cluster type :

quantif = table(sobj_ic$sample_identifier) %>%
  as.data.frame.table() %>%
  `colnames<-`(c("Sample", "nb_cells"))

aquarius::plot_barplot(df = table(sobj_ic$sample_identifier,
                                  sobj_ic$cluster_type) %>%
                         as.data.frame.table() %>%
                         `colnames<-`(c("Sample", "Cell Type", "Number")),
                       x = "Sample", y = "Number", fill = "Cell Type",
                       position = position_stack()) +
  ggplot2::geom_label(data = quantif, inherit.aes = FALSE,
                      aes(x = .data$Sample, y = 50 + .data$nb_cells, label = .data$nb_cells),
                      label.size = 0, size = 5) +
  ggplot2::scale_fill_manual(values = unlist(color_markers),
                             breaks = names(color_markers),
                             name = "Cell Type") +
  ggplot2::scale_y_continuous(limits = c(0, 100 + max(quantif$nb_cells)),
                              expand = ggplot2::expansion(add = c(0, 0.05))) +
  ggplot2::theme(axis.title.y = element_blank(),
                 axis.line.x = element_line(colour = "lightgray"),
                 text = element_text(size = 15),
                 legend.position = "none")

Heatmap for macrophages :

subsobj = subset(sobj_ic, cluster_type == "macrophages")
features_oi = c("IL1B", "TNF",
                "HLA-DQA2", "HLA-DPA1", "HLA-DRB5",
                "HLA-A", "HLA-C", "B2M",
                "C1QA", "C1QB", "C1QC")

# Matrix
mat_expr = Seurat::GetAssayData(subsobj)
mat_expr = mat_expr[features_oi, ]
mat_expr = Matrix::t(mat_expr)
mat_expr = dynutils::scale_quantile(mat_expr) # between 0 and 1
mat_expr = Matrix::t(mat_expr)
dim(mat_expr) # genes x cells
## [1]  11 463
## Colors
list_colors = list()

# Heatmap
list_colors[["expression"]] = rev(RColorBrewer::brewer.pal(name = "RdBu", n = 9))

# Sample annotation (top annotation)
list_colors[["sample_type"]] = sample_type_colors
list_colors[["sample_identifier"]] = setNames(nm = sample_info$sample_identifier,
                                              sample_info$color)
# Cells order
column_order = subsobj@meta.data %>%
  dplyr::arrange(sample_type, sample_identifier) %>%
  rownames()
column_order = match(column_order, rownames(subsobj@meta.data))

# Heatmap
ha_top = HeatmapAnnotation(sample_type = subsobj$sample_type,
                           sample_identifier = subsobj$sample_identifier,
                           col = list(sample_type = list_colors[["sample_type"]],
                                      sample_identifier = list_colors[["sample_identifier"]]))

# Heatmap
ht = Heatmap(as.matrix(mat_expr),
             heatmap_legend_param = list(title = "Expression", at = c(0, 1), 
                                         labels = c("low", "high")),
             col = list_colors[["expression"]],
             top_annotation = ha_top,
             show_column_names = FALSE,
             column_order = column_order,
             column_gap = unit(2, "mm"),
             cluster_rows = FALSE,
             row_title = NULL,
             row_names_gp = grid::gpar(fontsize = 14, fontface = "plain"),
             use_raster = FALSE,
             show_heatmap_legend = TRUE,
             border = TRUE)

ComplexHeatmap::draw(ht,
                     merge_legend = TRUE,
                     heatmap_legend_side = "bottom",
                     annotation_legend_side = "bottom")

Heatmap for CD4 T cells :

subsobj = subset(sobj_ic, cluster_type == "CD4 T cells")
features_oi = c("GZMA", "KLRB1", "BTG1", "ZFP36", "NFKBIA", "TXNIP", "CXCR4", "IFNG", "IL17A")

# Matrix
mat_expr = Seurat::GetAssayData(subsobj)
mat_expr = mat_expr[features_oi, ]
mat_expr = Matrix::t(mat_expr)
mat_expr = dynutils::scale_quantile(mat_expr) # between 0 and 1
mat_expr = Matrix::t(mat_expr)
dim(mat_expr) # genes x cells
## [1]   9 848
## Colors
list_colors = list()

# Heatmap
list_colors[["expression"]] = rev(RColorBrewer::brewer.pal(name = "RdBu", n = 9))

# Sample annotation (top annotation)
list_colors[["sample_type"]] = sample_type_colors
list_colors[["sample_identifier"]] = setNames(nm = sample_info$sample_identifier,
                                              sample_info$color)
# Cells order
column_order = subsobj@meta.data %>%
  dplyr::arrange(sample_type, sample_identifier) %>%
  rownames()
column_order = match(column_order, rownames(subsobj@meta.data))

# Heatmap
ha_top = HeatmapAnnotation(sample_type = subsobj$sample_type,
                           sample_identifier = subsobj$sample_identifier,
                           col = list(sample_type = list_colors[["sample_type"]],
                                      sample_identifier = list_colors[["sample_identifier"]]))

# Heatmap
ht = Heatmap(as.matrix(mat_expr),
             heatmap_legend_param = list(title = "Expression", at = c(0, 1), 
                                         labels = c("low", "high")),
             col = list_colors[["expression"]],
             top_annotation = ha_top,
             show_column_names = FALSE,
             column_order = column_order,
             column_gap = unit(2, "mm"),
             cluster_rows = FALSE,
             row_title = NULL,
             row_names_gp = grid::gpar(fontsize = 14, fontface = "plain"),
             use_raster = FALSE,
             show_heatmap_legend = TRUE,
             border = TRUE)

ComplexHeatmap::draw(ht,
                     merge_legend = TRUE,
                     heatmap_legend_side = "left",
                     annotation_legend_side = "left")

HFSC

Settings

We load the HFSCs dataset :

sobj_hfsc = readRDS(paste0(data_dir, "/4_zoom/2_zoom_hfsc/hfsc_sobj.rds"))
sobj_hfsc
## An object of class Seurat 
## 15384 features across 1454 samples within 1 assay 
## Active assay: RNA (15384 features, 2000 variable features)
##  6 dimensional reductions calculated: RNA_pca, RNA_pca_24_tsne, RNA_pca_24_umap, harmony, harmony_24_umap, harmony_24_tsne

This is the projection name to visualize cells :

name2D = "harmony_24_tsne"

To represent results from differential expression, we load the analyses results :

list_results = readRDS(paste0(data_dir, "/4_zoom/2_zoom_hfsc/hfsc_list_results.rds"))

lapply(list_results, FUN = names)
## $cluster_0_8
## [1] "p_val"     "avg_logFC" "pct.1"     "pct.2"     "p_val_adj"
## 
## $cluster_2
## [1] "p_val"     "avg_logFC" "pct.1"     "pct.2"     "p_val_adj"
## 
## $cluster_1
## [1] "p_val"     "avg_logFC" "pct.1"     "pct.2"     "p_val_adj"
## 
## $cluster_3
## [1] "p_val"     "avg_logFC" "pct.1"     "pct.2"     "p_val_adj"

Figures

HFSCs on the full atlas :

sobj$is_hfsc = (colnames(sobj) %in% colnames(sobj_hfsc))

Seurat::DimPlot(sobj, reduction = name2D_atlas, pt.size = 0.000001,
                group.by = "is_hfsc", order = "TRUE") +
  ggplot2::scale_color_manual(values = c(color_markers[["HFSC"]], bg_color),
                              breaks = c(TRUE, FALSE)) +
  ggplot2::labs(title = "HFSCs",
                subtitle = paste0(ncol(sobj_hfsc), " cells")) +
  ggplot2::theme(aspect.ratio = 1,
                 plot.title = element_text(hjust = 0.5, face = "bold"),
                 plot.subtitle = element_text(hjust = 0.5)) +
  Seurat::NoAxes() + Seurat::NoLegend()

KRT15 expression :

Seurat::FeaturePlot(sobj, reduction = name2D_atlas, pt.size = 0.000001,
                    features = "KRT15") +
  ggplot2::scale_color_gradientn(colors = aquarius::color_gene) +
  ggplot2::theme(aspect.ratio = 1) +
  Seurat::NoAxes() + Seurat::NoLegend()

Genes of interest :

genes = c("TGFB2", "ANGPTL7", "FGF18", "MGP", "EPCAM", "KRT75", "NOTCH3", "PTHLH")

plot_list = lapply(c(1:length(genes)), FUN = function(gene_id) {
  gene = genes[[gene_id]]
  
  sobj_hfsc$my_gene = Seurat::FetchData(sobj_hfsc, gene)[, 1] %>%
    aquarius::run_rescale(., new_min = 0, new_max = 10)
  
  Seurat::FeaturePlot(sobj_hfsc, features = "my_gene", reduction = name2D) +
    ggplot2::scale_color_gradientn(colors = aquarius::color_gene,
                                   breaks = seq(0, 10, by = 2.5),
                                   labels = c("min", rep("", 3), "max")) +
    ggplot2::labs(title = gene) +
    ggplot2::theme(aspect.ratio = 1,
                   plot.title = element_text(hjust = 0.5, size = 17),
                   plot.subtitle = element_text(hjust = 0.5, size = 15),
                   legend.text = element_text(size = 15),
                   legend.position = "none") +
    Seurat::NoAxes()
})

plot_list
## [[1]]

## 
## [[2]]

## 
## [[3]]

## 
## [[4]]

## 
## [[5]]

## 
## [[6]]

## 
## [[7]]

## 
## [[8]]

Project name :

# Random order
set.seed(1234)
rnd_order = sample(colnames(sobj_hfsc), replace = FALSE, size = ncol(sobj_hfsc))

# Extract coordinates
cells_coord = sobj_hfsc@reductions[[name2D]]@cell.embeddings %>%
  as.data.frame() %>%
  `colnames<-`(c("Dim1", "Dim2"))
cells_coord$project_name = sobj_hfsc$project_name
cells_coord = cells_coord[(rnd_order), ]

# Plot
ggplot2::ggplot(cells_coord, aes(x = Dim1, y = Dim2, col = project_name)) +
  ggplot2::geom_point(size = 1.2) +
  ggplot2::scale_color_manual(values = sample_info$color,
                              breaks = sample_info$project_name) +
  ggplot2::theme_void() +
  ggplot2::theme(aspect.ratio = 1,
                 legend.position = "none")

Cluster :

Seurat::DimPlot(sobj_hfsc, reduction = name2D, pt.size = 1,
                group.by = "seurat_clusters", cols = grey_palette,
                label = TRUE, label.size = 7) +
  ggplot2::theme(aspect.ratio = 1) +
  Seurat::NoAxes() +
  Seurat::NoLegend()

Heatmap with proportions :

cluster_markers = c("TGFB2", "ANGPTL7", "EPCAM", "KRT75", "NOTCH3", "PTHLH")

## Bottom annotation : gene expression by cluster
ht_annot = Seurat::FetchData(sobj_hfsc, slot = "data", vars = cluster_markers) %>%
  as.data.frame()
ht_annot$clusters = sobj_hfsc$seurat_clusters
ht_annot = ht_annot %>%
  dplyr::group_by(clusters) %>%
  dplyr::summarise_all(funs('mean' = mean)) %>%
  as.data.frame() %>%
  dplyr::select(-clusters) %>%
  `colnames<-`(c(cluster_markers))

ha_bottom = ComplexHeatmap::HeatmapAnnotation(df = ht_annot,
                                              which = "column",
                                              show_legend = TRUE,
                                              col = setNames(nm = cluster_markers,
                                                             lapply(cluster_markers, FUN = color_fun)),
                                              annotation_name_side = "left")

## Right annotation : number of cells by dataset
ht_annot = table(sobj_hfsc$sample_identifier) %>%
  as.data.frame.table() %>%
  `colnames<-`(c("sample_identifier", "nb_cells")) %>%
  `rownames<-`(.$sample_identifier) %>%
  dplyr::select(-sample_identifier)

ha_right = ComplexHeatmap::HeatmapAnnotation(
  df = ht_annot,
  which = "row",
  show_legend = TRUE,
  annotation_name_side = "bottom",
  col = list(nb_cells  = circlize::colorRamp2(colors = RColorBrewer::brewer.pal(name = "Greys", n = 9),
                                              breaks = seq(from = range(ht_annot$nb_cells)[1],
                                                           to = range(ht_annot$nb_cells)[2],
                                                           length.out = 9))))

## Heatmap
ht = aquarius::plot_prop_heatmap(df = sobj_hfsc@meta.data[, c("sample_identifier", "seurat_clusters")],
                                 bottom_annotation = ha_bottom,
                                 # right_annotation = ha_right,
                                 cluster_rows = TRUE,
                                 column_names_centered = TRUE,
                                 prop_margin = 1,
                                 row_names_gp = grid::gpar(names = sample_info$sample_identifier,
                                                           col = sample_info$color,
                                                           fontface = "bold"),
                                 row_title = "Sample",
                                 column_title = "Cluster")

ComplexHeatmap::draw(ht,
                     merge_legend = TRUE,
                     heatmap_legend_side = "bottom")

Heatmap for cluster 0 and 8 :

subsobj = subset(sobj_hfsc, seurat_clusters %in% c(0,8))

features_oi = rownames(list_results$cluster_0_8)
features_oi = features_oi[!grepl(features_oi, pattern = "^RP")]

# Matrix
mat_expr = Seurat::GetAssayData(subsobj)
mat_expr = mat_expr[features_oi, ]
mat_expr = Matrix::t(mat_expr)
mat_expr = cbind(mat_expr, subsobj$percent.mt)
colnames(mat_expr)[ncol(mat_expr)] = "percent.rb"
mat_expr = dynutils::scale_quantile(mat_expr) # between 0 and 1
mat_expr = Matrix::t(mat_expr)
dim(mat_expr) # genes x cells
## [1]  48 631
## Colors
list_colors = list()

# Heatmap
list_colors[["expression"]] = rev(RColorBrewer::brewer.pal(name = "RdBu", n = 9))

# Sample annotation (top annotation)
list_colors[["sample_type"]] = sample_type_colors
list_colors[["sample_identifier"]] = setNames(nm = sample_info$sample_identifier,
                                              sample_info$color)
# list_colors[["seurat_clusters"]] = setNames(aquarius::gg_color_hue(length(levels(subsobj$seurat_clusters))),
#                                             nm = levels(subsobj$seurat_clusters))
# Cells order
column_order = subsobj@meta.data %>%
  dplyr::arrange(sample_type, sample_identifier) %>%
  rownames()
column_order = match(column_order, rownames(subsobj@meta.data))

# Heatmap
ha_top = HeatmapAnnotation(sample_type = subsobj$sample_type,
                           sample_identifier = subsobj$sample_identifier,
                           # clusters = subsobj$seurat_clusters,
                           col = list(sample_type = list_colors[["sample_type"]],
                                      sample_identifier = list_colors[["sample_identifier"]]
                                      # clusters = list_colors[["seurat_clusters"]]
                           ))


# g1 : REACTOME_CYTOKINE_SIGNALING_IN_IMMUNE_SYSTEM
# g2 : GOBP_APOPTOTIC_PROCESS
g1_genes = c("B2M", "HLA-C", "HLA-A", "MIF", "PPIA", "JUNB", "IFITM3")
g2_genes = c("Jun", "ATF3", "BTG2", "RHOB", "NFKBIA", "SGK1", "KLF9",
             "CAV1", "DDIT4", "PDK4", "TXNIP", "RNF1152", "TLE1")
ha_right = data.frame(genes =  c(features_oi, "percent.rb"), rownames = c(features_oi, "percent.rb"))
ha_right$group = case_when(ha_right$genes %in% g1_genes ~ "REACTOME_CYTOKINE_SIGNALING_IN_IMMUNE_SYSTEM",
                           ha_right$genes %in% g2_genes ~ "GOBP_APOPTOTIC_PROCESS",
                           TRUE ~ "others")

list_colors[["group"]] = setNames(
  nm = c("REACTOME_CYTOKINE_SIGNALING_IN_IMMUNE_SYSTEM", "GOBP_APOPTOTIC_PROCESS", "others"),
  c("red", "black", "gray90"))

ha_right = HeatmapAnnotation(group = ha_right$group,
                             col = list(group = list_colors[["group"]]),
                             which = "row",
                             show_annotation_name = FALSE,
                             show_legend = TRUE)

# Heatmap
ht = Heatmap(as.matrix(mat_expr),
             heatmap_legend_param = list(title = "Expression", at = c(0, 1), 
                                         labels = c("low", "high")),
             col = list_colors[["expression"]],
             top_annotation = ha_top,
             right_annotation = ha_right,
             show_column_names = FALSE,
             column_order = column_order,
             column_gap = unit(2, "mm"),
             cluster_rows = FALSE,
             row_title = NULL,
             row_names_gp = grid::gpar(fontsize = 10, fontface = "plain"),
             use_raster = FALSE,
             show_heatmap_legend = TRUE,
             border = TRUE)

ComplexHeatmap::draw(ht,
                     merge_legend = TRUE,
                     heatmap_legend_side = "bottom",
                     annotation_legend_side = "bottom")

Violin plot of IFITM3 :

table(subsobj$sample_type)
## 
##  HS  HD 
## 588  43
ifitm3_hs = subsobj@assays$RNA@data["IFITM3", subsobj$sample_type == "HS"]
ifitm3_hd = subsobj@assays$RNA@data["IFITM3", subsobj$sample_type == "HD"]
ifitm3_hs_VS_ifitm3_hd = stats::t.test(ifitm3_hs, ifitm3_hd)
ifitm3_hs_VS_ifitm3_hd
## 
##  Welch Two Sample t-test
## 
## data:  ifitm3_hs and ifitm3_hd
## t = 7.0204, df = 47.675, p-value = 7.081e-09
## alternative hypothesis: true difference in means is not equal to 0
## 95 percent confidence interval:
##  0.5509680 0.9933324
## sample estimates:
## mean of x mean of y 
##  1.775647  1.003497
Seurat::VlnPlot(subsobj, group.by = "sample_type",
                features = "IFITM3", cols = sample_type_colors) +
  ggplot2::theme(axis.title.x = element_blank(),
                 legend.position = "none")

Split by sample :

Seurat::VlnPlot(subsobj, group.by = "sample_identifier",
                features = "IFITM3", cols = sample_info$color) +
  ggplot2::theme(axis.title.x = element_blank(),
                 legend.position = "none")

Violin plot of DDIT4 :

table(subsobj$sample_type)
## 
##  HS  HD 
## 588  43
DDIT4_hs = subsobj@assays$RNA@data["DDIT4", subsobj$sample_type == "HS"]
DDIT4_hd = subsobj@assays$RNA@data["DDIT4", subsobj$sample_type == "HD"]
DDIT4_hs_VS_DDIT4_hd = stats::t.test(DDIT4_hs, DDIT4_hd)
DDIT4_hs_VS_DDIT4_hd
## 
##  Welch Two Sample t-test
## 
## data:  DDIT4_hs and DDIT4_hd
## t = -11.338, df = 46.368, p-value = 5.774e-15
## alternative hypothesis: true difference in means is not equal to 0
## 95 percent confidence interval:
##  -2.614197 -1.826081
## sample estimates:
## mean of x mean of y 
## 0.9376455 3.1577846
Seurat::VlnPlot(subsobj, group.by = "sample_type",
                features = "DDIT4", cols = sample_type_colors) +
  ggplot2::theme(axis.title.x = element_blank(),
                 legend.position = "none")

Split by sample :

Seurat::VlnPlot(subsobj, group.by = "sample_identifier",
                features = "DDIT4", cols = sample_info$color) +
  ggplot2::theme(axis.title.x = element_blank(),
                 legend.position = "none")

We represent some genes split by sample type :

plot_list = lapply(c("DDIT4", "IFITM3"), FUN = function(one_gene) {
  p = aquarius::plot_split_dimred(sobj_hfsc,
                                  reduction = name2D,
                                  split_by = "sample_type",
                                  color_by = one_gene,
                                  color_palette = c("gray70", "#FDBB84", "#EF6548", "#7F0000", "black"),
                                  main_pt_size = 0.6,
                                  bg_pt_size = 0.6,
                                  order = TRUE,
                                  bg_color = "gray95")
  p = patchwork::wrap_plots(p, nrow = 1) +
    patchwork::plot_layout(guides = "collect") +
    ggplot2::theme(legend.position = "right") &
    ggplot2::theme(plot.subtitle = element_blank())
  return(p)
})

patchwork::wrap_plots(plot_list, ncol = 2)

Barplot with number of HFSCs and total number of cells :

quantif = dplyr::left_join(
  x = table(sobj$sample_identifier) %>%
    as.data.frame.table() %>%
    `colnames<-`(c("Sample", "nb_cells")),
  y = table(sobj_hfsc$sample_identifier) %>%
    as.data.frame.table() %>%
    `colnames<-`(c("Sample", "nb_hfsc")),
  by = "Sample") %>%
  dplyr::mutate(prop_hfsc = round(100*nb_hfsc / nb_cells, 2))

quantif_to_plot = rbind.data.frame(
  data.frame(Sample = quantif$Sample,
             nb_cells = quantif$nb_cells - quantif$nb_hfsc,
             cell_type = "others",
             stringsAsFactors = FALSE),
  data.frame(Sample = quantif$Sample,
             nb_cells = quantif$nb_hfsc,
             cell_type = "hfsc",
             stringsAsFactors = FALSE)) %>%
  dplyr::mutate(cell_type = factor(cell_type, levels = c("others", "hfsc")))

aquarius::plot_barplot(df = quantif_to_plot,
                       x = "Sample", y = "nb_cells", fill = "cell_type",
                       position = position_fill()) +
  ggplot2::geom_label(data = quantif, inherit.aes = FALSE,
                      aes(x = .data$Sample, y = 0.05+.data$prop_hfsc/100, label = .data$nb_hfsc),
                      label.size = 0, size = 5, fill = NA) +
  ggplot2::scale_fill_manual(values = c("gray90", color_markers[["HFSC"]]),
                             breaks = c("others", "hfsc"),
                             name = "Cell Type") +
  ggplot2::scale_y_continuous(breaks = seq(0, 1, by = 0.25),
                              labels = paste0(seq(0, 100, by = 25), sep = " %"),
                              expand = ggplot2::expansion(add = c(0, 0.05))) +
  ggplot2::theme(axis.title.y = element_blank(),
                 axis.line.x = element_line(colour = "lightgray"),
                 text = element_text(size = 15),
                 legend.position = "none")

IBL and ORS

Settings

We load the IBL + ORS dataset :

sobj_iblors = readRDS(paste0(data_dir, "/4_zoom/3_zoom_iblmors/iblmors_sobj.rds"))
sobj_iblors
## An object of class Seurat 
## 16701 features across 3532 samples within 1 assay 
## Active assay: RNA (16701 features, 2000 variable features)
##  6 dimensional reductions calculated: RNA_pca, RNA_pca_20_tsne, RNA_pca_20_umap, harmony, harmony_20_umap, harmony_20_tsne

This is the projection name to visualize cells :

name2D = "harmony_20_tsne"

To represent results from differential expression, we load the analyses results :

list_results = readRDS(paste0(data_dir, "/4_zoom/3_zoom_iblmors/iblmors_list_results.rds"))

lapply(list_results, FUN = names)
## $IBL_vs_ORS
## [1] "mark"        "enrichr_ibl" "enrichr_ors" "gsea"       
## 
## $cluster5_vs_ORS
## [1] "mark"       "enrichr_up" "enrichr_dn" "gsea"      
## 
## $IBL_HS_vs_HD
## [1] "mark"       "enrichr_hs" "enrichr_hd" "gsea"      
## 
## $ORS_HS_vs_HD
## [1] "mark"       "enrichr_hs" "enrichr_hd" "gsea"

Preparation

We defined cluster type :

cluster_type = table(sobj_iblors$cell_type, sobj_iblors$seurat_clusters) %>%
  prop.table(., margin = 2) %>%
  apply(., 2, which.max)
cluster_type = setNames(nm = names(cluster_type),
                        levels(sobj_iblors$cell_type)[cluster_type])

sobj_iblors$cluster_type = cluster_type[sobj_iblors$seurat_clusters]
sobj_iblors$cluster_type = factor(sobj_iblors$cluster_type,
                                  levels = c("IBL", "ORS"))

Figures

IBL + ORS on the full atlas :

sobj$cell_bc = colnames(sobj)
sobj_iblors$cell_bc = colnames(sobj_iblors)
sobj$is_iblors = dplyr::left_join(sobj@meta.data[, c("cell_bc", "percent.mt")],
                                  sobj_iblors@meta.data[, c("cell_bc", "cluster_type")],
                                  by = "cell_bc")[, "cluster_type"]

Seurat::DimPlot(sobj, reduction = name2D_atlas, pt.size = 0.000001,
                group.by = "is_iblors", order = "TRUE") +
  ggplot2::scale_color_manual(values = c(color_markers[c("IBL", "ORS")], bg_color),
                              breaks = c("IBL", "ORS", NA), na.value = bg_color) +
  ggplot2::labs(title = "IBL + ORS",
                subtitle = paste0(ncol(sobj_iblors), " cells")) +
  ggplot2::theme(aspect.ratio = 1,
                 plot.title = element_text(hjust = 0.5, face = "bold"),
                 plot.subtitle = element_text(hjust = 0.5)) +
  Seurat::NoAxes() + Seurat::NoLegend()

Project name :

# Random order
set.seed(1234)
rnd_order = sample(colnames(sobj_iblors), replace = FALSE, size = ncol(sobj_iblors))

# Extract coordinates
cells_coord = sobj_iblors@reductions[[name2D]]@cell.embeddings %>%
  as.data.frame() %>%
  `colnames<-`(c("Dim1", "Dim2"))
cells_coord$project_name = sobj_iblors$project_name
cells_coord = cells_coord[(rnd_order), ]

# Plot
ggplot2::ggplot(cells_coord, aes(x = Dim1, y = Dim2, col = project_name)) +
  ggplot2::geom_point(size = 1.2) +
  ggplot2::scale_color_manual(values = sample_info$color,
                              breaks = sample_info$project_name) +
  ggplot2::theme_void() +
  ggplot2::theme(aspect.ratio = 1,
                 legend.position = "none")

Cluster :

Seurat::DimPlot(sobj_iblors, reduction = name2D, pt.size = 1,
                group.by = "seurat_clusters", cols = grey_palette,
                label = TRUE, label.size = 8) +
  ggplot2::theme(aspect.ratio = 1) +
  Seurat::NoAxes() +
  Seurat::NoLegend()

Cluster type :

Seurat::DimPlot(sobj_iblors, reduction = name2D, pt.size = 1,
                group.by = "cluster_type", cols = color_markers) +
  ggplot2::theme(aspect.ratio = 1) +
  Seurat::NoAxes() +
  Seurat::NoLegend()

Cluster type split by sample type :

plot_list = aquarius::plot_split_dimred(sobj_iblors, reduction = name2D,
                                        group_by = "cluster_type",
                                        group_color = color_markers,
                                        split_by = "sample_type",
                                        bg_pt_size = 1, main_pt_size = 1,
                                        bg_color = bg_color)

patchwork::wrap_plots(plot_list) &
  Seurat::NoLegend()

Barplot by cluster family :

sobj_iblors$cluster_type_sep5 = ifelse(sobj_iblors$seurat_clusters == 5,
                                       yes = "ORS_5",
                                       no = as.character(sobj_iblors$cluster_type)) %>%
  as.factor()

quantif = table(sobj_iblors$sample_identifier) %>%
  as.data.frame.table() %>%
  `colnames<-`(c("Sample", "nb_cells"))

quantif_to_plot = table(sobj_iblors$sample_identifier,
                        sobj_iblors$cluster_type_sep5) %>%
  as.data.frame.table() %>%
  `colnames<-`(c("Sample", "CellType", "Number")) %>%
  dplyr::mutate(Style = ifelse(CellType == "ORS_5", yes = "IL1R2+", no = "IL1R2-")) %>%
  dplyr::mutate(Style = factor(Style, levels = c("IL1R2-", "IL1R2+"))) %>%
  dplyr::mutate(CellType = ifelse(CellType == "ORS_5", yes = "ORS", no = as.character(CellType))) %>%
  `colnames<-`(c("Sample", "Cell Type", "Number", "IL1R2 status"))

aquarius::plot_barplot(df = quantif_to_plot,
                       x = "Sample", y = "Number",
                       fill = "Cell Type", pattern = "IL1R2 status",
                       position = position_fill()) +
  ggplot2::geom_label(data = quantif, inherit.aes = FALSE,
                      aes(x = .data$Sample, y = 1.05, label = .data$nb_cells),
                      label.size = 0, size = 5) +
  ggplot2::scale_fill_manual(values = unlist(color_markers[levels(sobj_iblors$cluster_type)]),
                             breaks = names(color_markers[levels(sobj_iblors$cluster_type)]),
                             name = "Cell Type") +
  ggplot2::scale_y_continuous(breaks = seq(0, 1, by = 0.25),
                              labels = paste0(seq(0, 100, by = 25), sep = " %"),
                              expand = ggplot2::expansion(add = c(0, 0.05))) +
  ggplot2::theme(axis.title.y = element_blank(),
                 axis.line.x = element_line(colour = "lightgray"),
                 text = element_text(size = 15),
                 legend.position = "right")

DE genes between IBL and ORS :

mark = list_results$IBL_vs_ORS$mark
mark$gene_name = rownames(mark)
mark_label = rbind(
  # up-regulated in IBL
  mark %>% dplyr::top_n(., n = 20, wt = avg_logFC),
  # up-regulated in ORS
  mark %>% dplyr::top_n(., n = 20, wt = -avg_logFC),
  # representative and selective for IBL
  mark %>% dplyr::top_n(., n = 20, wt = (pct.1 - pct.2)),
  # representative and selective for ORS
  mark %>% dplyr::top_n(., n = 20, wt = -(pct.1 - pct.2))) %>%
  dplyr::distinct()
mark_label = mark_label[!grepl(rownames(mark_label), pattern = "^MT"), ]

avg_logFC_range = setNames(c(min(mark_label$avg_logFC), -1, 0, 1, max(mark_label$avg_logFC)),
                           nm = c("dodgerblue4", "dodgerblue3", "#B7B7B7", "firebrick3", "firebrick4"))


ggplot2::ggplot(mark, aes(x = pct.1, y = pct.2, col = avg_logFC)) +
  ggplot2::geom_abline(slope = 1, intercept = 0, lty = 2) +
  ggplot2::geom_point() +
  ggrepel::geom_label_repel(data = mark_label, max.overlaps = Inf,
                            aes(x = pct.1, y = pct.2, label = gene_name),
                            col = "black", fill = NA, size = 3.5, label.size = NA) +
  ggplot2::labs(x = "Enriched in IBL",
                y = "Enriched in ORS") +
  ggplot2::scale_color_gradientn(colors = names(avg_logFC_range),
                                 values = scales::rescale(unname(avg_logFC_range))) +
  ggplot2::theme_classic() +
  ggplot2::theme(aspect.ratio = 1)

GSEA plot :

the_gs_name = "REACTOME_KERATINIZATION" 
the_content = list_results$IBL_vs_ORS$gsea@result %>%
  dplyr::filter(ID == the_gs_name)
the_subtitle = paste0("\nNES : ", round(the_content$NES, 2),
                      " | pvalue : ", round(the_content$pvalue, 4),
                      " | set size : ", the_content$setSize, " genes")

enrichplot::gseaplot2(x = list_results$IBL_vs_ORS$gsea,
                      geneSetID = the_gs_name) +
  ggplot2::labs(title = the_gs_name,
                subtitle = the_subtitle) +
  ggplot2::theme(plot.title = element_text(hjust = 0.5, face = "bold",
                                           margin = ggplot2::margin(3, 3, 5, 3)),
                 plot.subtitle = ggtext::element_markdown(hjust = 0.5,
                                                          size = 10))

the_gs_name = "HALLMARK_INTERFERON_GAMMA_RESPONSE" 
the_content = list_results$IBL_vs_ORS$gsea@result %>%
  dplyr::filter(ID == the_gs_name)
the_subtitle = paste0("\nNES : ", round(the_content$NES, 2),
                      " | pvalue : ", round(the_content$pvalue, 4),
                      " | set size : ", the_content$setSize, " genes")

enrichplot::gseaplot2(x = list_results$IBL_vs_ORS$gsea,
                      geneSetID = the_gs_name) +
  ggplot2::labs(title = the_gs_name,
                subtitle = the_subtitle) +
  ggplot2::theme(plot.title = element_text(hjust = 0.5, face = "bold",
                                           margin = ggplot2::margin(3, 3, 5, 3)),
                 plot.subtitle = ggtext::element_markdown(hjust = 0.5,
                                                          size = 10))

Score for both gene sets, in all cells :

gene_sets = aquarius::get_gene_sets(species = "Homo sapiens")
the_gs_name = "REACTOME_KERATINIZATION" 
the_gs_content = gene_sets$gene_sets_full %>%
  dplyr::filter(gs_name == the_gs_name) %>%
  dplyr::pull(gene_symbol) %>%
  unlist()

sobj_iblors$score_kera = Seurat::AddModuleScore(sobj_iblors,
                                                features = list(the_gs_content))$Cluster1

Seurat::VlnPlot(sobj_iblors, features = "score_kera", pt.size = 0.05,
                split.by = "sample_type", group.by = "cluster_type",
                cols = rev(sample_type_colors)) +
  ggplot2::labs(title = the_gs_name) +
  ggplot2::theme(axis.title.x = element_blank())

the_gs_name = "HALLMARK_INTERFERON_GAMMA_RESPONSE" 
the_gs_content = gene_sets$gene_sets_full %>%
  dplyr::filter(gs_name == the_gs_name) %>%
  dplyr::pull(gene_symbol) %>%
  unlist()

sobj_iblors$score_ifna = Seurat::AddModuleScore(sobj_iblors,
                                                features = list(the_gs_content))$Cluster1

Seurat::VlnPlot(sobj_iblors, features = "score_ifna", pt.size = 0.05,
                split.by = "sample_type", group.by = "cluster_type",
                cols = rev(sample_type_colors)) +
  ggplot2::labs(title = the_gs_name) +
  ggplot2::theme(axis.title.x = element_blank())

Violin plot for IBL :

subsobj = subset(sobj_iblors, cluster_type == "IBL")

Seurat::VlnPlot(subsobj, group.by = "sample_type", pt.size = 0.05,
                features = c("DUSP1", "DDIT4", "MIF", "LGALS7", "ARF5", "S100A9"),
                cols = sample_type_colors, ncol = 6) &
  ggplot2::theme(axis.title.x = element_blank(),
                 axis.title.y = element_blank(),
                 legend.position = "none")

Violin plot for ORS :

subsobj = subset(sobj_iblors, cluster_type == "ORS")

Seurat::VlnPlot(subsobj, group.by = "sample_type", pt.size = 0.05,
                features = c("DUSP1", "KLF6", "CLDN1", "CTGF",
                             "S100A9", "CCL2", "IFITM3", "IFI27"),
                cols = sample_type_colors, ncol = 8) &
  ggplot2::theme(axis.title.x = element_blank(),
                 axis.title.y = element_blank(),
                 legend.position = "none")

Split by sample :

Seurat::VlnPlot(subsobj, group.by = "sample_identifier",
                features = "IFI27", cols = sample_info$color) +
  ggplot2::theme(axis.title.x = element_blank(),
                 legend.position = "none")

Heatmap for cluster 5 vs other ORS :

subsobj = subset(sobj_iblors, cluster_type == "ORS")

features_oi = c("YBX3", "TXNIP", "KRT14", "KRT15", "NEAT1",
                "FXYD3", "MT2A", "MT1E", "MT1X", "AQP3", "GLUL",
                # "HALLMARK_TNFA_SIGNALING_VIA_NFKB"
                "FOS", "JUNB", "DUSP1", "ZFP36", "NFKBIZ",
                "ATF3", "RHOB",  "ETS2", "IL18", "KLF4", "KLF6", "KLF9",
                "KLF3", "KLF5", "COL17A1", "THSD4", "WNT3", "WNT4", "SLPI", "PLAT",
                "LAMB4", "DCN", "SPINK5",
                "GSTM3", "ALDH3A1",  "LGALS7B", "SLC38A2", "EHF",  "CLEC2B",
                "IL20RB", "IL1R2", "IFI27", "CXCL14", "HLA-C", "GPSM2", "DAAM1",   "ID1",
                "RNASET2", "HOPX", "POU3F1", "SPRY1", "AR", "PDGFC",
                "WFDC2", "WFDC5", "TSC22D3", "FGFR3",  "LY6D", "IGFBP3", 
                # Other ORS
                "APOE", "CTSB", "CALD1", "SOX4",
                "STMN1", "LMO4", "CEBPB", "TMEM45A", "GPX2", "C1QTNF12", "GJB6",
                "KRT6A", "KRT17", "RBP1", "CALML3", "PTN", "DAPK2",
                "EGLN3", "FILIP1L", "ADGRL3", "FST", "EFNB2", "SEMA5A",
                "FGFR1", "EGR2", "CLDN1", "DEFB1", "CARD18", "MGST1")

# Matrix
mat_expr = Seurat::GetAssayData(subsobj)
mat_expr = mat_expr[features_oi, ]
mat_expr = Matrix::t(mat_expr)
mat_expr = dynutils::scale_quantile(mat_expr) # between 0 and 1
mat_expr = Matrix::t(mat_expr)
dim(mat_expr) # genes x cells
## [1]   89 2022
## Colors
list_colors = list()
list_colors[["expression"]] = rev(RColorBrewer::brewer.pal(name = "RdBu", n = 9))
list_colors[["sample_type"]] = sample_type_colors
list_colors[["sample_identifier"]] = setNames(nm = sample_info$sample_identifier,
                                              sample_info$color)
list_colors[["population"]] = setNames(nm = c("IL1R2+ ORS", "other ORS"),
                                       c("black", color_markers["ORS"]))
list_colors[["nFeature_RNA"]] = circlize::colorRamp2(breaks = seq(from = min(subsobj$nFeature_RNA),
                                                                  to = max(subsobj$nFeature_RNA),
                                                                  length.out = 9),
                                                     colors = RColorBrewer::brewer.pal(name = "Greys", n = 9))

# Cells order
column_order = subsobj@meta.data %>%
  dplyr::mutate(seurat_clusters = factor(seurat_clusters, levels = c(5, 3, 0, 1, 7))) %>%
  dplyr::arrange(sample_type, seurat_clusters, sample_identifier) %>%
  rownames()
column_order = match(column_order, rownames(subsobj@meta.data))

# Annotation
ha_top = HeatmapAnnotation(sample_type = subsobj$sample_type,
                           sample_identifier = subsobj$sample_identifier,
                           population = ifelse(subsobj$cluster_type_sep5 == "ORS",
                                               yes = "other ORS", no = "IL1R2+ ORS"),
                           col = list(sample_type = list_colors[["sample_type"]],
                                      sample_identifier = list_colors[["sample_identifier"]],
                                      population = list_colors[["population"]]))

ha_bottom = HeatmapAnnotation(nFeature_RNA = subsobj$nFeature_RNA,
                              col = list(nFeature_RNA = list_colors[["nFeature_RNA"]]))

# Heatmap
ht = Heatmap(as.matrix(mat_expr),
             heatmap_legend_param = list(title = "Expression", at = c(0, 1), 
                                         labels = c("low", "high")),
             col = list_colors[["expression"]],
             top_annotation = ha_top,
             bottom_annotation = ha_bottom,
             # Cell grouping
             column_split = subsobj$sample_type %>% as.character(),
             cluster_columns = FALSE,
             column_order = column_order,
             column_title = NULL,
             show_column_dend = FALSE,
             show_column_names = FALSE,
             # Genes
             cluster_rows = FALSE,
             row_names_gp = grid::gpar(fontsize = 14, fontface = "plain"),
             # Style
             use_raster = FALSE,
             show_heatmap_legend = TRUE,
             border = TRUE)

ComplexHeatmap::draw(ht,
                     merge_legend = TRUE,
                     heatmap_legend_side = "right",
                     annotation_legend_side = "right")

Genes of interest :

genes = c("KRT16", "COL17A1", "DST", "KRT6B", "IL1R2", "WNT3",
          "IFI27", "CXCL14", "IGFBP3", "KRT15", "CD200")

plot_list = lapply(c(1:length(genes)), FUN = function(gene_id) {
  gene = genes[[gene_id]]
  
  sobj_iblors$my_gene = Seurat::FetchData(sobj_iblors, gene)[, 1] %>%
    aquarius::run_rescale(., new_min = 0, new_max = 10)
  
  Seurat::FeaturePlot(sobj_iblors, features = "my_gene", reduction = name2D, pt.size = 0.25) +
    ggplot2::scale_color_gradientn(colors = aquarius::color_gene,
                                   breaks = seq(0, 10, by = 2.5),
                                   labels = c("min", rep("", 3), "max")) +
    ggplot2::labs(title = gene) +
    ggplot2::theme(aspect.ratio = 1,
                   plot.title = element_text(hjust = 0.5, size = 17),
                   plot.subtitle = element_text(hjust = 0.5, size = 15),
                   legend.text = element_text(size = 15),
                   legend.position = "none") +
    Seurat::NoAxes()
})

plot_list
## [[1]]

## 
## [[2]]

## 
## [[3]]

## 
## [[4]]

## 
## [[5]]

## 
## [[6]]

## 
## [[7]]

## 
## [[8]]

## 
## [[9]]

## 
## [[10]]

## 
## [[11]]

HFSCs to IBL and ORS

Settings

We load the merged dataset :

sobj_traj = readRDS(paste0(data_dir, "/4_zoom/4_zoom_hfsc_iblmors/hfsc_iblmors_sobj_traj_tinga.rds"))
sobj_traj
## An object of class Seurat 
## 17050 features across 4986 samples within 1 assay 
## Active assay: RNA (17050 features, 2000 variable features)
##  8 dimensional reductions calculated: RNA_pca, RNA_pca_18_tsne, RNA_pca_18_umap, harmony, harmony_18_umap, harmony_18_tsne, harmony_dm, harmony_dm_5_umap

This is the projection name to visualize cells :

name2D = "harmony_dm"

We load the trajectory object for visualisation purpose :

my_traj = readRDS(paste0(data_dir, "/4_zoom/4_zoom_hfsc_iblmors/hfsc_iblmors_my_traj_tinga.rds"))
class(my_traj)
## [1] "dynwrap::with_dimred"     "dynwrap::with_trajectory"
## [3] "dynwrap::data_wrapper"    "list"

Preparation

We defined cell type based on individual object :

sobj_iblors$cell_bc = colnames(sobj_iblors)
sobj_traj$cell_bc = colnames(sobj_traj)
sobj_traj$cluster_type = dplyr::left_join(sobj_traj@meta.data[, c("cell_bc", "percent.mt")],
                                          sobj_iblors@meta.data[, c("cell_bc", "cluster_type")],
                                          by = "cell_bc")[, "cluster_type"] %>% as.character()
sobj_traj$cluster_type = ifelse(colnames(sobj_traj) %in% colnames(sobj_hfsc),
                                yes = "HFSC",
                                no = sobj_traj$cluster_type) %>%
  as.factor()

Figures

Cells on the full atlas :

sobj$cell_bc = colnames(sobj)
sobj_traj$cell_bc = colnames(sobj_traj)
sobj$is_traj = dplyr::left_join(sobj@meta.data[, c("cell_bc", "percent.mt")],
                                sobj_traj@meta.data[, c("cell_bc", "cluster_type")],
                                by = "cell_bc")[, "cluster_type"]

Seurat::DimPlot(sobj, reduction = name2D_atlas, pt.size = 0.000001,
                group.by = "is_traj", order = levels(sobj_traj$cluster_type)) +
  ggplot2::scale_color_manual(values = c(color_markers[c("IBL", "ORS", "HFSC")], bg_color),
                              breaks = c("IBL", "ORS", "HFSC", NA), na.value = bg_color) +
  ggplot2::theme(aspect.ratio = 1) +
  Seurat::NoAxes() + Seurat::NoLegend()

Project name :

# Random order
set.seed(1234)
rnd_order = sample(colnames(sobj_traj), replace = FALSE, size = ncol(sobj_traj))

# Extract coordinates
cells_coord = sobj_traj@reductions[[name2D]]@cell.embeddings %>%
  as.data.frame() %>%
  `colnames<-`(c("Dim1", "Dim2"))
cells_coord$sample_type = sobj_traj$sample_type
cells_coord = cells_coord[order(sobj_traj$sample_type), ]

# Plot
ggplot2::ggplot(cells_coord, aes(x = Dim1, y = Dim2, col = sample_type)) +
  ggplot2::geom_point(size = 1.2) +
  ggplot2::scale_color_manual(values = sample_type_colors,
                              breaks = names(sample_type_colors)) +
  ggplot2::theme_void() +
  ggplot2::theme(aspect.ratio = 1,
                 legend.position = "none")

Cluster type :

Seurat::DimPlot(sobj_traj, reduction = name2D, pt.size = 0.5,
                group.by = "cluster_type", cols = color_markers) +
  ggplot2::theme(aspect.ratio = 1) +
  Seurat::NoAxes() +
  Seurat::NoLegend()

Pseudotime :

Seurat::FeaturePlot(sobj_traj, reduction = name2D, pt.size = 0.5,
                    features = "pseudotime") +
  ggplot2::scale_color_gradientn(colors = viridis::viridis(n = 100)) +
  ggplot2::lims(x = range(sobj_traj@reductions[[name2D]]@cell.embeddings[, 1]),
                y = range(sobj_traj@reductions[[name2D]]@cell.embeddings[, 2])) +
  ggplot2::theme(aspect.ratio = 1,
                 plot.title = element_blank()) +
  Seurat::NoAxes()

Pseudotime with dynplot’s function :

dynplot::plot_dimred(trajectory = my_traj,
                     dimred = sobj_traj[[name2D]]@cell.embeddings,
                     # Cells
                     color_cells = 'pseudotime',
                     size_cells = 1.6,
                     border_radius_percentage = 0,
                     # Trajectory
                     plot_trajectory = TRUE,
                     color_trajectory = "none",
                     label_milestones = FALSE,
                     size_milestones = 0,
                     size_transitions = 1)

OEP002321 dataset

Settings

We load the dataset containing all cells :

sobj = readRDS(paste0(data_dir, "/5_wu/3_combined/wu_sobj.rds"))
sobj
## An object of class Seurat 
## 17727 features across 17762 samples within 1 assay 
## Active assay: RNA (17727 features, 2000 variable features)
##  6 dimensional reductions calculated: RNA_pca, RNA_pca_38_tsne, RNA_pca_38_umap, harmony, harmony_38_umap, harmony_38_tsne

This is the projection name to visualize cells :

name2D = "harmony_38_tsne"
name2D_atlas = name2D

These are all the samples analyzed :

sample_info = readRDS(paste0(data_dir, "/5_wu/1_metadata/wu_sample_info.rds"))

# Nb cells by dataset
to_plot = table(sobj$sample_identifier) %>%
  as.data.frame.table(., stringsAsFactors = FALSE) %>%
  `colnames<-`(c("sample_identifier", "nb_cells")) %>%
  dplyr::left_join(x = ., y = sample_info, by = "sample_identifier") 

# patchwork
plot_list = aquarius::fig_plot_gb(to_plot, title = "Available datasets")
patchwork::wrap_plots(plot_list) +
  patchwork::plot_layout(design = "A\nB", heights = c(0.1,5)) &
  ggplot2::theme(plot.title = element_text(hjust = 0.5, face = "bold", size = 15))

We define cluster type and cluster family :

sobj$cell_type = sobj$cell_type %>%
  as.character() %>%
  factor(., levels = names(color_markers))

cluster_type = table(sobj$cell_type, sobj$seurat_clusters) %>%
  prop.table(., margin = 2) %>%
  apply(., 2, which.max)
cluster_type = setNames(nm = names(cluster_type),
                        levels(sobj$cell_type)[cluster_type])

sobj$cluster_type = cluster_type[sobj$seurat_clusters]
sobj$cluster_type = factor(sobj$cluster_type,
                           levels = levels(sobj$cell_type)) %>%
  base::droplevels()
sobj$cluster_family = custom_order_cell_type[sobj$cluster_type, "cell_family"]
sobj$cluster_family = factor(sobj$cluster_family,
                             levels = names(family_color))

Global figures

Project name :

# Random order
set.seed(1234)
rnd_order = sample(colnames(sobj), replace = FALSE, size = ncol(sobj))

# Extract coordinates
cells_coord = sobj@reductions[[name2D_atlas]]@cell.embeddings %>%
  as.data.frame() %>%
  `colnames<-`(c("Dim1", "Dim2"))
cells_coord$project_name = sobj$project_name
cells_coord = cells_coord[(rnd_order), ]

# Plot
ggplot2::ggplot(cells_coord, aes(x = Dim1, y = Dim2, col = project_name)) +
  ggplot2::geom_point(size = 0.5) +
  ggplot2::scale_color_manual(values = sample_info$color,
                              breaks = sample_info$project_name) +
  ggplot2::theme_void() +
  ggplot2::theme(aspect.ratio = 1,
                 legend.position = "none")

Cell type annotation :

Seurat::DimPlot(sobj, reduction = name2D, pt.size = 0.5,
                group.by = "cell_type", cols = color_markers) +
  ggplot2::theme(aspect.ratio = 1) +
  Seurat::NoAxes() +
  Seurat::NoLegend()

Cluster type annotation :

Seurat::DimPlot(sobj, reduction = name2D, pt.size = 0.5,
                group.by = "cluster_type", cols = color_markers) +
  ggplot2::theme(aspect.ratio = 1) +
  Seurat::NoAxes() +
  Seurat::NoLegend()

Gene expression to assess annotation :

genes = c("PTPRC", "MSX2", "KRT14")
names(genes) = c("immune cells", "matrix cells", "non-matrix cells")

plot_list = lapply(c(1:length(genes)), FUN = function(gene_id) {
  gene = genes[[gene_id]]
  pop = names(genes)[gene_id]
  
  sobj$my_gene = Seurat::FetchData(sobj, gene)[, 1] %>%
    aquarius::run_rescale(., new_min = 0, new_max = 10)
  
  Seurat::FeaturePlot(sobj, features = "my_gene", reduction = name2D_atlas) +
    ggplot2::scale_color_gradientn(colors = aquarius::color_gene,
                                   breaks = seq(0, 10, by = 2.5),
                                   labels = c("min", rep("", 3), "max")) +
    ggplot2::labs(title = gene) + 
    # subtitle = pop) +
    ggplot2::theme(aspect.ratio = 1,
                   plot.title = element_text(hjust = 0.5, size = 17),
                   plot.subtitle = element_text(hjust = 0.5, size = 15),
                   legend.text = element_text(size = 15),
                   legend.position = "none") +
    Seurat::NoAxes()
})

plot_list
## [[1]]

## 
## [[2]]

## 
## [[3]]

Dotplot :

custom_order_cell_type = custom_order_cell_type[levels(sobj$cluster_type), c("cell_type", "cell_family")]

plot_list = Seurat::DotPlot(sobj,
                            features = c("PTPRC",
                                         "CD3E", "CD4",
                                         "CD207", "AIF1",
                                         # "PRDM1", "KRT85",
                                         "MSX2",
                                         "KRT32", "KRT35",
                                         "KRT31", "PRR9",
                                         "BAMBI", "ALDH1A3",
                                         "KRT71", "KRT73",
                                         "TOP2A", "MCM5",
                                         "KRT14", "CXCL14",
                                         "KRT15", "COL17A1",
                                         "DIO2", "TCEAL2",
                                         "KRT16", "KRT6C",
                                         "SPINK5", "LY6D"),
                            group.by = "cluster_type", scale = TRUE,
                            scale.by = "radius", scale.min = NA, scale.max = NA) +
  ggplot2::scale_color_gradientn(colors = aquarius::color_gene) +
  ggplot2::theme(legend.position = "bottom",
                 legend.direction = "vertical",
                 # legend.justification = "bottom",
                 legend.box = "horizontal",
                 legend.box.margin = margin(0,25,0,0),
                 axis.title = element_blank(),
                 axis.ticks.y = element_blank(),
                 axis.text.y = element_blank(),
                 axis.line.y = element_blank(),
                 axis.text.x = element_text(angle = 45, hjust = 1, size = 11, color = "black"),
                 plot.margin = unit(c(0,0.5,0,0), "cm"))

p = ggplot2::ggplot(custom_order_cell_type, aes(y = cell_type, x = 0)) +
  ggplot2::geom_point(size = 0) +
  ggplot2::geom_segment(aes(y = 0.5, yend = 2.5, x = 0, xend = 0), size = 6, col = family_color["immune cells"]) +
  ggplot2::geom_segment(aes(y = 2.5, yend = 7.5, x = 0, xend = 0), size = 6, col = family_color["matrix"]) +
  ggplot2::geom_segment(aes(y = 7.5, yend = 11.5, x = 0, xend = 0), size = 6, col = family_color["non matrix"]) +
  ggplot2::scale_x_continuous(expand = c(0,0), limits = c(0,0)) +
  ggplot2::theme_classic() +
  ggplot2::theme(axis.text.x = element_blank(),
                 axis.ticks.x = element_blank(),
                 axis.title = element_blank(),
                 axis.line.x = element_blank(),
                 axis.text.y = element_text(size = 12, color = "black"),
                 plot.margin = unit(c(0.5,0,0,0.5), "cm"))

plot_list = patchwork::wrap_plots(p, plot_list,
                                  nrow = 1, widths = c(1, 25))
plot_list

IBL and ORS dataset

We load the IBL + ORS dataset :

sobj_iblors = readRDS(paste0(data_dir, "/5_wu/4_ibl_ors/iblmors_sobj.rds"))
sobj_iblors
## An object of class Seurat 
## 15541 features across 8026 samples within 1 assay 
## Active assay: RNA (15541 features, 2000 variable features)
##  6 dimensional reductions calculated: RNA_pca, RNA_pca_20_tsne, RNA_pca_20_umap, harmony, harmony_20_umap, harmony_20_tsne

This is the projection name to visualize cells :

name2D = "harmony_20_tsne"

To represent results from differential expression, we load the analyses results :

list_results = readRDS(paste0(data_dir, "/5_wu/4_ibl_ors/iblmors_list_results.rds"))

lapply(list_results, FUN = names)
## $IBL_vs_ORS
## [1] "mark"        "enrichr_ibl" "enrichr_ors" "gsea"

We defined cluster type :

cluster_type = table(sobj_iblors$cell_type, sobj_iblors$seurat_clusters) %>%
  prop.table(., margin = 2) %>%
  apply(., 2, which.max)
cluster_type = setNames(nm = names(cluster_type),
                        levels(sobj_iblors$cell_type)[cluster_type])

sobj_iblors$cluster_type = cluster_type[sobj_iblors$seurat_clusters]
sobj_iblors$cluster_type = factor(sobj_iblors$cluster_type,
                                  levels = c("IBL", "ORS"))

IBL + ORS figure

IBL + ORS on the full atlas :

sobj$cell_bc = colnames(sobj)
sobj_iblors$cell_bc = colnames(sobj_iblors)
sobj$is_iblors = dplyr::left_join(sobj@meta.data[, c("cell_bc", "percent.mt")],
                                  sobj_iblors@meta.data[, c("cell_bc", "cluster_type")],
                                  by = "cell_bc")[, "cluster_type"]

Seurat::DimPlot(sobj, reduction = name2D_atlas, pt.size = 0.000001,
                group.by = "is_iblors", order = FALSE) +
  ggplot2::scale_color_manual(values = c(color_markers[c("IBL", "ORS")], bg_color),
                              breaks = c("IBL", "ORS", NA), na.value = bg_color) +
  ggplot2::theme(aspect.ratio = 1,
                 plot.title = element_blank()) +
  Seurat::NoAxes() + Seurat::NoLegend()

Cluster type :

Seurat::DimPlot(sobj_iblors, reduction = name2D, pt.size = 1,
                group.by = "cluster_type", cols = color_markers) +
  ggplot2::theme(aspect.ratio = 1) +
  Seurat::NoAxes() +
  Seurat::NoLegend()

DE genes between IBL and ORS :

mark = list_results$IBL_vs_ORS$mark
mark$gene_name = rownames(mark)
mark_label = rbind(
  # up-regulated in IBL
  mark %>% dplyr::top_n(., n = 20, wt = avg_logFC),
  # up-regulated in ORS
  mark %>% dplyr::top_n(., n = 20, wt = -avg_logFC),
  # representative and selective for IBL
  mark %>% dplyr::top_n(., n = 20, wt = (pct.1 - pct.2)),
  # representative and selective for ORS
  mark %>% dplyr::top_n(., n = 20, wt = -(pct.1 - pct.2))) %>%
  dplyr::distinct()
mark_label = mark_label[!grepl(rownames(mark_label), pattern = "^MT"), ]

avg_logFC_range = setNames(c(min(mark_label$avg_logFC), -1, 0, 1, max(mark_label$avg_logFC)),
                           nm = c("dodgerblue4", "dodgerblue3", "#B7B7B7", "firebrick3", "firebrick4"))


ggplot2::ggplot(mark, aes(x = pct.1, y = pct.2, col = avg_logFC)) +
  ggplot2::geom_abline(slope = 1, intercept = 0, lty = 2) +
  ggplot2::geom_point() +
  ggrepel::geom_label_repel(data = mark_label, max.overlaps = Inf,
                            aes(x = pct.1, y = pct.2, label = gene_name),
                            col = "black", fill = NA, size = 3.5, label.size = NA) +
  ggplot2::labs(x = "Enriched in IBL",
                y = "Enriched in ORS") +
  ggplot2::scale_color_gradientn(colors = names(avg_logFC_range),
                                 values = scales::rescale(unname(avg_logFC_range))) +
  ggplot2::theme_classic() +
  ggplot2::theme(aspect.ratio = 1)

GSEA plot :

the_gs_name = "REACTOME_KERATINIZATION" 
the_content = list_results$IBL_vs_ORS$gsea@result %>%
  dplyr::filter(ID == the_gs_name)
the_subtitle = paste0("\nNES : ", round(the_content$NES, 2),
                      " | pvalue : ", round(the_content$pvalue, 4),
                      " | set size : ", the_content$setSize, " genes")

enrichplot::gseaplot2(x = list_results$IBL_vs_ORS$gsea,
                      geneSetID = the_gs_name) +
  ggplot2::labs(title = the_gs_name,
                subtitle = the_subtitle) +
  ggplot2::theme(plot.title = element_text(hjust = 0.5, face = "bold",
                                           margin = ggplot2::margin(3, 3, 5, 3)),
                 plot.subtitle = ggtext::element_markdown(hjust = 0.5,
                                                          size = 10))

the_gs_name = "HALLMARK_INTERFERON_GAMMA_RESPONSE" 
the_content = list_results$IBL_vs_ORS$gsea@result %>%
  dplyr::filter(ID == the_gs_name)
the_subtitle = paste0("\nNES : ", round(the_content$NES, 2),
                      " | pvalue : ", round(the_content$pvalue, 4),
                      " | set size : ", the_content$setSize, " genes")

enrichplot::gseaplot2(x = list_results$IBL_vs_ORS$gsea,
                      geneSetID = the_gs_name) +
  ggplot2::labs(title = the_gs_name,
                subtitle = the_subtitle) +
  ggplot2::theme(plot.title = element_text(hjust = 0.5, face = "bold",
                                           margin = ggplot2::margin(3, 3, 5, 3)),
                 plot.subtitle = ggtext::element_markdown(hjust = 0.5,
                                                          size = 10))

Genes of interest :

genes = c("KRT16", "COL17A1", "DST", "KRT6B", "IL1R2", "WNT3",
          "IFI27", "CXCL14", "IGFBP3", "KRT15", "CD200")

plot_list = lapply(c(1:length(genes)), FUN = function(gene_id) {
  gene = genes[[gene_id]]
  
  sobj_iblors$my_gene = Seurat::FetchData(sobj_iblors, gene)[, 1] %>%
    aquarius::run_rescale(., new_min = 0, new_max = 10)
  
  Seurat::FeaturePlot(sobj_iblors, features = "my_gene", reduction = name2D, pt.size = 0.25) +
    ggplot2::scale_color_gradientn(colors = aquarius::color_gene,
                                   breaks = seq(0, 10, by = 2.5),
                                   labels = c("min", rep("", 3), "max")) +
    ggplot2::labs(title = gene) +
    ggplot2::theme(aspect.ratio = 1,
                   plot.title = element_text(hjust = 0.5, size = 17),
                   plot.subtitle = element_text(hjust = 0.5, size = 15),
                   legend.text = element_text(size = 15),
                   legend.position = "none") +
    Seurat::NoAxes()
})

plot_list
## [[1]]

## 
## [[2]]

## 
## [[3]]

## 
## [[4]]

## 
## [[5]]

## 
## [[6]]

## 
## [[7]]

## 
## [[8]]

## 
## [[9]]

## 
## [[10]]

## 
## [[11]]

R Session

show
## R version 3.6.3 (2020-02-29)
## Platform: x86_64-pc-linux-gnu (64-bit)
## Running under: Ubuntu 20.04.6 LTS
## 
## Matrix products: default
## BLAS:   /usr/local/lib/R/lib/libRblas.so
## LAPACK: /usr/local/lib/R/lib/libRlapack.so
## 
## locale:
## [1] C
## 
## attached base packages:
##  [1] parallel  stats4    grid      stats     graphics  grDevices utils    
##  [8] datasets  methods   base     
## 
## other attached packages:
##  [1] org.Mm.eg.db_3.10.0   AnnotationDbi_1.48.0  IRanges_2.20.2       
##  [4] S4Vectors_0.24.4      Biobase_2.46.0        BiocGenerics_0.32.0  
##  [7] ComplexHeatmap_2.14.0 ggplot2_3.3.5         patchwork_1.1.2      
## [10] dplyr_1.0.7          
## 
## loaded via a namespace (and not attached):
##   [1] softImpute_1.4              graphlayouts_0.7.0         
##   [3] pbapply_1.4-2               lattice_0.20-41            
##   [5] haven_2.3.1                 dyndimred_1.0.3            
##   [7] vctrs_0.3.8                 usethis_2.0.1              
##   [9] dynwrap_1.2.1               blob_1.2.1                 
##  [11] survival_3.2-13             prodlim_2019.11.13         
##  [13] dynutils_1.0.5              later_1.3.0                
##  [15] DBI_1.1.1                   R.utils_2.11.0             
##  [17] SingleCellExperiment_1.8.0  rappdirs_0.3.3             
##  [19] uwot_0.1.8                  dqrng_0.2.1                
##  [21] jpeg_0.1-8.1                zlibbioc_1.32.0            
##  [23] pspline_1.0-18              pcaMethods_1.78.0          
##  [25] mvtnorm_1.1-1               htmlwidgets_1.5.4          
##  [27] GlobalOptions_0.1.2         future_1.22.1              
##  [29] UpSetR_1.4.0                laeken_0.5.2               
##  [31] leiden_0.3.3                clustree_0.4.3             
##  [33] lmds_0.1.0                  scater_1.14.6              
##  [35] irlba_2.3.3                 markdown_1.1               
##  [37] DEoptimR_1.0-9              tidygraph_1.1.2            
##  [39] Rcpp_1.0.9                  readr_2.0.2                
##  [41] KernSmooth_2.23-17          carrier_0.1.0              
##  [43] promises_1.1.0              gdata_2.18.0               
##  [45] DelayedArray_0.12.3         limma_3.42.2               
##  [47] graph_1.64.0                RcppParallel_5.1.4         
##  [49] Hmisc_4.4-0                 fs_1.5.2                   
##  [51] RSpectra_0.16-0             fastmatch_1.1-0            
##  [53] ranger_0.12.1               digest_0.6.25              
##  [55] png_0.1-7                   sctransform_0.2.1          
##  [57] cowplot_1.0.0               DOSE_3.12.0                
##  [59] here_1.0.1                  TInGa_0.0.0.9000           
##  [61] dynplot_1.1.0               ggraph_2.0.3               
##  [63] pkgconfig_2.0.3             GO.db_3.10.0               
##  [65] DelayedMatrixStats_1.8.0    gower_0.2.1                
##  [67] ggbeeswarm_0.6.0            iterators_1.0.12           
##  [69] DropletUtils_1.6.1          reticulate_1.26            
##  [71] clusterProfiler_3.14.3      SummarizedExperiment_1.16.1
##  [73] circlize_0.4.15             beeswarm_0.4.0             
##  [75] GetoptLong_1.0.5            xfun_0.35                  
##  [77] bslib_0.3.1                 zoo_1.8-10                 
##  [79] tidyselect_1.1.0            GA_3.2                     
##  [81] reshape2_1.4.4              purrr_0.3.4                
##  [83] ica_1.0-2                   pcaPP_1.9-73               
##  [85] viridisLite_0.3.0           rtracklayer_1.46.0         
##  [87] rlang_1.0.2                 hexbin_1.28.1              
##  [89] jquerylib_0.1.4             dyneval_0.9.9              
##  [91] glue_1.4.2                  waldo_0.3.1                
##  [93] RColorBrewer_1.1-2          matrixStats_0.56.0         
##  [95] stringr_1.4.0               lava_1.6.7                 
##  [97] europepmc_0.3               DESeq2_1.26.0              
##  [99] recipes_0.1.17              labeling_0.3               
## [101] httpuv_1.5.2                class_7.3-17               
## [103] BiocNeighbors_1.4.2         DO.db_2.9                  
## [105] annotate_1.64.0             jsonlite_1.7.2             
## [107] XVector_0.26.0              bit_4.0.4                  
## [109] mime_0.9                    aquarius_0.1.5             
## [111] Rsamtools_2.2.3             gridExtra_2.3              
## [113] gplots_3.0.3                stringi_1.4.6              
## [115] processx_3.5.2              gsl_2.1-6                  
## [117] bitops_1.0-6                cli_3.0.1                  
## [119] batchelor_1.2.4             RSQLite_2.2.0              
## [121] randomForest_4.6-14         tidyr_1.1.4                
## [123] data.table_1.14.2           rstudioapi_0.13            
## [125] units_0.7-2                 GenomicAlignments_1.22.1   
## [127] nlme_3.1-147                qvalue_2.18.0              
## [129] scran_1.14.6                locfit_1.5-9.4             
## [131] scDblFinder_1.1.8           listenv_0.8.0              
## [133] ggthemes_4.2.4              gridGraphics_0.5-0         
## [135] R.oo_1.24.0                 dbplyr_1.4.4               
## [137] TTR_0.24.2                  readxl_1.3.1               
## [139] lifecycle_1.0.1             timeDate_3043.102          
## [141] ggpattern_0.3.1             munsell_0.5.0              
## [143] cellranger_1.1.0            R.methodsS3_1.8.1          
## [145] proxyC_0.1.5                visNetwork_2.0.9           
## [147] caTools_1.18.0              codetools_0.2-16           
## [149] GenomeInfoDb_1.22.1         vipor_0.4.5                
## [151] lmtest_0.9-38               msigdbr_7.5.1              
## [153] htmlTable_1.13.3            triebeard_0.3.0            
## [155] lsei_1.2-0                  xtable_1.8-4               
## [157] ROCR_1.0-7                  classInt_0.4-3             
## [159] BiocManager_1.30.10         scatterplot3d_0.3-41       
## [161] abind_1.4-5                 farver_2.0.3               
## [163] parallelly_1.28.1           RANN_2.6.1                 
## [165] askpass_1.1                 GenomicRanges_1.38.0       
## [167] RcppAnnoy_0.0.16            tibble_3.1.5               
## [169] ggdendro_0.1-20             cluster_2.1.0              
## [171] future.apply_1.5.0          Seurat_3.1.5               
## [173] dendextend_1.15.1           Matrix_1.3-2               
## [175] ellipsis_0.3.2              prettyunits_1.1.1          
## [177] lubridate_1.7.9             ggridges_0.5.2             
## [179] igraph_1.2.5                RcppEigen_0.3.3.7.0        
## [181] fgsea_1.12.0                remotes_2.4.2              
## [183] scBFA_1.0.0                 destiny_3.0.1              
## [185] VIM_6.1.1                   testthat_3.1.0             
## [187] htmltools_0.5.2             BiocFileCache_1.10.2       
## [189] yaml_2.2.1                  utf8_1.1.4                 
## [191] plotly_4.9.2.1              XML_3.99-0.3               
## [193] ModelMetrics_1.2.2.2        e1071_1.7-3                
## [195] foreign_0.8-76              withr_2.5.0                
## [197] fitdistrplus_1.0-14         BiocParallel_1.20.1        
## [199] xgboost_1.4.1.1             bit64_4.0.5                
## [201] foreach_1.5.0               robustbase_0.93-9          
## [203] Biostrings_2.54.0           GOSemSim_2.13.1            
## [205] rsvd_1.0.3                  memoise_2.0.0              
## [207] evaluate_0.18               forcats_0.5.0              
## [209] rio_0.5.16                  geneplotter_1.64.0         
## [211] tzdb_0.1.2                  caret_6.0-86               
## [213] ps_1.6.0                    DiagrammeR_1.0.6.1         
## [215] curl_4.3                    fdrtool_1.2.15             
## [217] fansi_0.4.1                 highr_0.8                  
## [219] urltools_1.7.3              xts_0.12.1                 
## [221] GSEABase_1.48.0             acepack_1.4.1              
## [223] edgeR_3.28.1                checkmate_2.0.0            
## [225] scds_1.2.0                  cachem_1.0.6               
## [227] npsurv_0.4-0                babelgene_22.3             
## [229] rjson_0.2.20                openxlsx_4.1.5             
## [231] ggrepel_0.9.1               clue_0.3-60                
## [233] rprojroot_2.0.2             stabledist_0.7-1           
## [235] tools_3.6.3                 sass_0.4.0                 
## [237] nichenetr_1.1.1             magrittr_2.0.1             
## [239] RCurl_1.98-1.2              proxy_0.4-24               
## [241] car_3.0-11                  ape_5.3                    
## [243] ggplotify_0.0.5             xml2_1.3.2                 
## [245] httr_1.4.2                  assertthat_0.2.1           
## [247] rmarkdown_2.18              boot_1.3-25                
## [249] globals_0.14.0              R6_2.4.1                   
## [251] Rhdf5lib_1.8.0              nnet_7.3-14                
## [253] RcppHNSW_0.2.0              progress_1.2.2             
## [255] genefilter_1.68.0           statmod_1.4.34             
## [257] gtools_3.8.2                shape_1.4.6                
## [259] sf_1.0-3                    HDF5Array_1.14.4           
## [261] BiocSingular_1.2.2          rhdf5_2.30.1               
## [263] splines_3.6.3               AUCell_1.8.0               
## [265] carData_3.0-4               colorspace_1.4-1           
## [267] generics_0.1.0              base64enc_0.1-3            
## [269] dynfeature_1.0.0            smoother_1.1               
## [271] gridtext_0.1.1              pillar_1.6.3               
## [273] tweenr_1.0.1                sp_1.4-1                   
## [275] ggplot.multistats_1.0.0     rvcheck_0.1.8              
## [277] GenomeInfoDbData_1.2.2      plyr_1.8.6                 
## [279] gtable_0.3.0                zip_2.2.0                  
## [281] knitr_1.41                  latticeExtra_0.6-29        
## [283] biomaRt_2.42.1              fastmap_1.1.0              
## [285] ADGofTest_0.3               copula_1.0-0               
## [287] doParallel_1.0.15           vcd_1.4-8                  
## [289] babelwhale_1.0.1            openssl_1.4.1              
## [291] scales_1.1.1                backports_1.2.1            
## [293] ipred_0.9-12                enrichplot_1.6.1           
## [295] hms_1.1.1                   ggforce_0.3.1              
## [297] Rtsne_0.15                  shiny_1.7.1                
## [299] gridpattern_0.3.1           numDeriv_2016.8-1.1        
## [301] polyclip_1.10-0             lazyeval_0.2.2             
## [303] Formula_1.2-3               tsne_0.1-3                 
## [305] crayon_1.3.4                MASS_7.3-54                
## [307] pROC_1.16.2                 viridis_0.5.1              
## [309] dynparam_1.0.0              rpart_4.1-15               
## [311] zinbwave_1.8.0              compiler_3.6.3             
## [313] ggtext_0.1.0
LS0tCnRpdGxlOiAiSFMgcHJvamVjdCIKc3VidGl0bGU6ICJGaWd1cmVzIgphdXRob3I6ICJBdWRyZXkiCmRhdGU6ICJgciBmb3JtYXQoU3lzLnRpbWUoKSwgJyVZLSVtLSVkJylgIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIGNvZGVfZm9sZGluZzogc2hvdwogICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIG51bWJlcl9zZWN0aW9uczogZmFsc2UKLS0tCgo8c3R5bGU+CmJvZHkgewp0ZXh0LWFsaWduOiBqdXN0aWZ5fQo8L3N0eWxlPgoKPCEtLSBBdXRvbWF0aWNhbGx5IGNvbXB1dGVzIGFuZCBwcmludHMgaW4gdGhlIG91dHB1dCB0aGUgcnVubmluZyB0aW1lIGZvciBhbnkgY29kZSBjaHVuayAtLT4KYGBge3IsIGVjaG89RkFMU0V9CiMgaHR0cHM6Ly9naXRodWIuY29tL3JzdHVkaW8vcm1hcmtkb3duL2lzc3Vlcy8xNDUzCmhvb2tzID0ga25pdHI6OmtuaXRfaG9va3MkZ2V0KCkKaG9va19mb2xkYWJsZSA9IGZ1bmN0aW9uKHR5cGUpIHsKICBmb3JjZSh0eXBlKQogIGZ1bmN0aW9uKHgsIG9wdGlvbnMpIHsKICAgIHJlcyA9IGhvb2tzW1t0eXBlXV0oeCwgb3B0aW9ucykKICAgIAogICAgaWYgKGlzRkFMU0Uob3B0aW9uc1tbcGFzdGUwKCJmb2xkXyIsIHR5cGUpXV0pKSByZXR1cm4ocmVzKQogICAgCiAgICBwYXN0ZTAoCiAgICAgICI8ZGV0YWlscz48c3VtbWFyeT4iLCAic2hvdyIsICI8L3N1bW1hcnk+XG5cbiIsCiAgICAgIHJlcywKICAgICAgIlxuXG48L2RldGFpbHM+IgogICAgKQogIH0KfQprbml0cjo6a25pdF9ob29rcyRzZXQoCiAgb3V0cHV0ID0gaG9va19mb2xkYWJsZSgib3V0cHV0IiksCiAgcGxvdCA9IGhvb2tfZm9sZGFibGUoInBsb3QiKSwKICB0aW1lX2l0ID0gbG9jYWwoewogICAgbm93ID0gTlVMTAogICAgZnVuY3Rpb24oYmVmb3JlLCBvcHRpb25zKSB7CiAgICAgIGlmIChvcHRpb25zJHRpbWVfaXQpIHsKICAgICAgICBpZiAoYmVmb3JlKSB7CiAgICAgICAgICBub3cgPD0gU3lzLnRpbWUoKQogICAgICAgIH0gZWxzZSB7CiAgICAgICAgICByZXMgPSBkaWZmdGltZShTeXMudGltZSgpLCBub3csIHVuaXRzID0gInNlY3MiKQogICAgICAgICAgcGFzdGUoIihUaW1lIHRvIHJ1biA6Iiwgcm91bmQocmVzLCBkaWdpdHMgPSAyKSwgInMpIikKICAgICAgICB9CiAgICAgIH0KICAgIH0KICB9KQopCmBgYAoKPCEtLSBTZXQgZGVmYXVsdCBwYXJhbWV0ZXJzIGZvciBhbGwgY2h1bmtzIC0tPgpgYGB7ciwgc2V0dXAsIGluY2x1ZGUgPSBGQUxTRX0Kc2V0LnNlZWQoMTMzN0wpCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgIyBkaXNwbGF5IGNvZGUKICAgICAgICAgICAgICAgICAgICAgICMgZGlzcGxheSBjaHVuayBvdXRwdXQKICAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2UgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgIHdhcm5pbmcgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgIGZvbGRfb3V0cHV0ID0gRkFMU0UsICMgdXNlZnVsIGZvciBzZXNzaW9uSW5mbygpCiAgICAgICAgICAgICAgICAgICAgICBmb2xkX3Bsb3QgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgIyBmaWd1cmUgc2V0dGluZ3MKICAgICAgICAgICAgICAgICAgICAgIGZpZy5hbGlnbiA9ICdjZW50ZXInLAogICAgICAgICAgICAgICAgICAgICAgZmlnLndpZHRoID0gMjAsCiAgICAgICAgICAgICAgICAgICAgICBmaWcuaGVpZ2h0ID0gMTUsCiAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICMgc29tZXRoaW5nIGFib3V0IHNlZWQsIGNodW5rIGFuZCBSbWFya2Rvd24gY29tcGlsYXRpb24KICAgICAgICAgICAgICAgICAgICAgICMgaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvMzk0MTcwMDMvbG9uZy12ZWN0b3JzLW5vdC1zdXBwb3J0ZWQteWV0LWVycm9yLWluLXJtZC1idXQtbm90LWluLXItc2NyaXB0CiAgICAgICAgICAgICAgICAgICAgICAjIGNhY2hlID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgIGNhY2hlLmxhenkgPSBGQUxTRSwgCiAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICMgYWRkIHJ1bnRpbWUgYWZ0ZXIgY2h1bmsKICAgICAgICAgICAgICAgICAgICAgIHRpbWVfaXQgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgIyBzYXZlIGZpZ3VyZXMgaW4gUERGIGluIGEgc2VwYXJhdGUgZm9sZGVyCiAgICAgICAgICAgICAgICAgICAgICBkZXYgPSBjKCdwbmcnLCAncGRmJyksICMgdGlmZiBvciBwZGYgYWxvbmUgcmVuZGVycyBiYWQgaW4gaHRtbAogICAgICAgICAgICAgICAgICAgICAgIyBkcGkgPSAzMDAsCiAgICAgICAgICAgICAgICAgICAgICBmaWcucGF0aCA9ICJmaWd1cmVzX2RldGFpbC8iLAogICAgICAgICAgICAgICAgICAgICAgcGRmLm9wdGlvbnMoZW5jb2RpbmcgPSAiSVNPTGF0aW45LmVuYyIpKQpgYGAKCgpUaGlzIGZpbGUgaXMgdXNlZCB0byBwcmVwYXJlIHRoZSBmaWd1cmVzIGZvciB0aGUgcGFwZXIuCgpgYGB7ciBsaWJyYXJ5fQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHBhdGNod29yaykKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KENvbXBsZXhIZWF0bWFwKQpsaWJyYXJ5KG9yZy5NbS5lZy5kYikKCi5saWJQYXRocygpCmBgYAoKCiMgUHJlcGFyYXRpb24KCkhlcmUgYXJlIHRoZSBmb2xkZXJzIHdoZXJlIGFuYWx5emVzIGFyZSBzdG9yZWQgOgoKYGBge3IgbG9jYXRpb25zfQpkYXRhX2RpciA9ICIuLy4uIgpsaXN0LmZpbGVzKGRhdGFfZGlyKQpgYGAKCgpXZSBsb2FkIHRoZSBkYXRhc2V0IGNvbnRhaW5pbmcgYWxsIGNlbGxzIDoKCmBgYHtyIGxvYWRfYWxsX3NvYmp9CnNvYmogPSByZWFkUkRTKHBhc3RlMChkYXRhX2RpciwgIi8zX2NvbWJpbmVkL2hzX2hkX3NvYmoucmRzIikpCnNvYmoKYGBgCgpUaGVzZSBhcmUgYWxsIHRoZSBzYW1wbGVzIGFuYWx5emVkIDoKCmBgYHtyIHNhbXBsZV9pbmZvLCBjbGFzcy5zb3VyY2UgPSAnZm9sZC1oaWRlJywgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDN9CnNhbXBsZV9pbmZvID0gcmVhZFJEUyhwYXN0ZTAoZGF0YV9kaXIsICIvMV9tZXRhZGF0YS9oc19oZF9zYW1wbGVfaW5mby5yZHMiKSkKCiMgTmIgY2VsbHMgYnkgZGF0YXNldAp0b19wbG90ID0gdGFibGUoc29iaiRzYW1wbGVfaWRlbnRpZmllcikgJT4lCiAgYXMuZGF0YS5mcmFtZS50YWJsZSguLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpICU+JQogIGBjb2xuYW1lczwtYChjKCJzYW1wbGVfaWRlbnRpZmllciIsICJuYl9jZWxscyIpKSAlPiUKICBkcGx5cjo6bGVmdF9qb2luKHggPSAuLCB5ID0gc2FtcGxlX2luZm8sIGJ5ID0gInNhbXBsZV9pZGVudGlmaWVyIikgCgojIHBhdGNod29yawpwbG90X2xpc3QgPSBhcXVhcml1czo6ZmlnX3Bsb3RfZ2IodG9fcGxvdCwgdGl0bGUgPSAiQXZhaWxhYmxlIGRhdGFzZXRzIikKcGF0Y2h3b3JrOjp3cmFwX3Bsb3RzKHBsb3RfbGlzdCkgKwogIHBhdGNod29yazo6cGxvdF9sYXlvdXQoZGVzaWduID0gIkFcbkIiLCBoZWlnaHRzID0gYygwLjEsNSkpICYKICBnZ3Bsb3QyOjp0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBmYWNlID0gImJvbGQiLCBzaXplID0gMTUpKQpgYGAKClRoZXNlIGFyZSB0aGUgY3VzdG9tIGNvbG9ycyBmb3IgY2VsbCBwb3B1bGF0aW9ucyA6CgpgYGB7ciBjb2xvcl9tYXJrZXJzLCBmaWcud2lkdGggPSAxMCwgZmlnLmhlaWdodCA9IDEsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUifQpjb2xvcl9tYXJrZXJzID0gcmVhZFJEUyhwYXN0ZTAoZGF0YV9kaXIsICIvMV9tZXRhZGF0YS9oc19oZF9jb2xvcl9tYXJrZXJzLnJkcyIpKQpjb2xvcl9tYXJrZXJzID0gY29sb3JfbWFya2Vyc1tuYW1lcyhjb2xvcl9tYXJrZXJzKSAhPSAibWVsYW5vY3l0ZXMiXQpvcnNfY29sb3IgPSBjb2xvcl9tYXJrZXJzWyJPUlMiXQpjb2xvcl9tYXJrZXJzWyJPUlMiXSA9IGNvbG9yX21hcmtlcnNbIklGRSJdIApjb2xvcl9tYXJrZXJzWyJJRkUiXSA9IG9yc19jb2xvcgpjb2xvcl9tYXJrZXJzWyJCIGNlbGxzIl0gPSAiY2hvY29sYXRlMyIKcm0ob3JzX2NvbG9yKQoKIyByZS1vcmRlcgpjb2xvcl9tYXJrZXJzID0gY29sb3JfbWFya2Vyc1tjKCJDRDQgVCBjZWxscyIsICJDRDggVCBjZWxscyIsICJMYW5nZXJoYW5zIGNlbGxzIiwgIm1hY3JvcGhhZ2VzIiwgIkIgY2VsbHMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJjdXRpY2xlIiwgImNvcnRleCIsICJtZWR1bGxhIiwgIklSUyIsICJwcm9saWZlcmF0aXZlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiSEZTQyIsICJPUlMiLCAiSUJMIiwgIklGRSIsICJzZWJvY3l0ZXMiKV0KCmRhdGEuZnJhbWUoY2VsbF90eXBlID0gbmFtZXMoY29sb3JfbWFya2VycyksCiAgICAgICAgICAgY29sb3IgPSB1bmxpc3QoY29sb3JfbWFya2VycykpICU+JQogIGdncGxvdDI6OmdncGxvdCguLCBhZXMoeCA9IGNlbGxfdHlwZSwgeSA9IDAsIGZpbGwgPSBjZWxsX3R5cGUpKSArCiAgZ2dwbG90Mjo6Z2VvbV9wb2ludChwY2ggPSAyMSwgc2l6ZSA9IDUpICsKICBnZ3Bsb3QyOjpzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSB1bmxpc3QoY29sb3JfbWFya2VycyksIGJyZWFrcyA9IG5hbWVzKGNvbG9yX21hcmtlcnMpKSArCiAgZ2dwbG90Mjo6dGhlbWVfY2xhc3NpYygpICsKICBnZ3Bsb3QyOjp0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgICAgICAgICAgICAgICAgYXhpcy5saW5lID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDEsIGFuZ2xlID0gMjApKQpgYGAKCldlIGRlZmluZSBjdXN0b20gY29sb3JzIGZvciBzYW1wbGUgdHlwZSA6CgpgYGB7ciBzYW1wbGVfdHlwZV9jb2xvcnMsIGZpZy53aWR0aCA9IDMsIGZpZy5oZWlnaHQgPSAwLjc1LCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1oaWRlIn0Kc2FtcGxlX3R5cGVfY29sb3JzID0gc2V0TmFtZXMobm0gPSBsZXZlbHMoc2FtcGxlX2luZm8kc2FtcGxlX3R5cGUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjKCIjQzU1RjQwIiwgIiMyQzc4RTYiKSkKCmRhdGEuZnJhbWUoY2VsbF90eXBlID0gbmFtZXMoc2FtcGxlX3R5cGVfY29sb3JzKSwKICAgICAgICAgICBjb2xvciA9IHVubGlzdChzYW1wbGVfdHlwZV9jb2xvcnMpKSAlPiUKICBnZ3Bsb3QyOjpnZ3Bsb3QoLiwgYWVzKHggPSBjZWxsX3R5cGUsIHkgPSAwLCBmaWxsID0gY2VsbF90eXBlKSkgKwogIGdncGxvdDI6Omdlb21fcG9pbnQocGNoID0gMjEsIHNpemUgPSA1KSArCiAgZ2dwbG90Mjo6c2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gdW5saXN0KHNhbXBsZV90eXBlX2NvbG9ycyksIGJyZWFrcyA9IG5hbWVzKHNhbXBsZV90eXBlX2NvbG9ycykpICsKICBnZ3Bsb3QyOjp0aGVtZV9jbGFzc2ljKCkgKwogIGdncGxvdDI6OnRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwKICAgICAgICAgICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICBheGlzLnRpY2tzID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF9ibGFuaygpKQpgYGAKCldlIHNldCBhIGJhY2tncm91bmQgY29sb3IgOgoKYGBge3IgYmdfY29sb3J9CmJnX2NvbG9yID0gImdyYXk5NCIKYGBgCgoKVGhpcyBpcyB0aGUgY29ycmVzcG9uZGVuY2UgYmV0d2VlbiBjZWxsIHR5cGVzIGFuZCBjZWxsIGZhbWlsaWVzLCBhbmQgY3VzdG9tIGNvbG9ycyB0byBjb2xvciBjZWxscyBieSBjZWxsIGZhbWlseSA6CgpgYGB7ciBjZWxsX2ZhbWlseX0KY3VzdG9tX29yZGVyX2NlbGxfdHlwZSA9IGRhdGEuZnJhbWUoCiAgY2VsbF90eXBlID0gbmFtZXMoY29sb3JfbWFya2VycyksCiAgY2VsbF9mYW1pbHkgPSBjKHJlcCgiaW1tdW5lIGNlbGxzIiwgNSksCiAgICAgICAgICAgICAgICAgIHJlcCgibWF0cml4IiwgNSksCiAgICAgICAgICAgICAgICAgIHJlcCgibm9uIG1hdHJpeCIsIDUpKSwKICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCmN1c3RvbV9vcmRlcl9jZWxsX3R5cGUkY2VsbF90eXBlID0gZmFjdG9yKGN1c3RvbV9vcmRlcl9jZWxsX3R5cGUkY2VsbF90eXBlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjdXN0b21fb3JkZXJfY2VsbF90eXBlJGNlbGxfdHlwZSkKcm93bmFtZXMoY3VzdG9tX29yZGVyX2NlbGxfdHlwZSkgPSBjdXN0b21fb3JkZXJfY2VsbF90eXBlJGNlbGxfdHlwZQoKZmFtaWx5X2NvbG9yID0gYygiaW1tdW5lIGNlbGxzIiA9ICJzbGF0ZWJsdWUxIiwKICAgICAgICAgICAgICAgICAibWF0cml4IiA9ICJtZWRpdW1zZWFncmVlbiIsCiAgICAgICAgICAgICAgICAgIm5vbiBtYXRyaXgiID0gImZpcmVicmljazMiKQpgYGAKCldlIGxvYWQgbWFya2VycyB0byBkaXNwbGF5IG9uIGEgZG90cGxvdCB0byBhc3Nlc3MgY2VsbCB0eXBlIGFubm90YXRpb24gOgoKYGBge3IgZG90cGxvdF9tYXJrZXJzfQpkb3RwbG90X21hcmtlcnMgPSByZWFkUkRTKHBhc3RlMChkYXRhX2RpciwgIi8xX21ldGFkYXRhL2hzX2hkX2RvdHBsb3RfbWFya2Vycy5yZHMiKSkKZG90cGxvdF9tYXJrZXJzID0gZG90cGxvdF9tYXJrZXJzW25hbWVzKGRvdHBsb3RfbWFya2VycykgIT0gIm1lbGFub2N5dGVzIl0KbGVuZ3Rocyhkb3RwbG90X21hcmtlcnMpCmBgYAoKQ3VzdG9tIGZ1bmN0aW9ucyB0byBkaXNwbGF5IGdlbmUgZXhwcmVzc2lvbiBvbiB0aGUgaGVhdG1hcCA6CgpgYGB7ciBjb2xvcl9mdW4sIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUifQpjb2xvcl9mdW4gPSBmdW5jdGlvbihvbmVfZ2VuZSkgewogIGdlbmVfcmFuZ2UgPSByYW5nZShodF9hbm5vdFssIG9uZV9nZW5lXSkKICBnZW5lX3BhbGV0dGUgPSBjaXJjbGl6ZTo6Y29sb3JSYW1wMihjb2xvcnMgPSBjKCIjRkZGRkZGIiwgYXF1YXJpdXM6OmNvbG9yX2dlbmVbLTFdKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBzZXEoZnJvbSA9IGdlbmVfcmFuZ2VbMV0sIHRvID0gZ2VuZV9yYW5nZVsyXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVuZ3RoLm91dCA9IGxlbmd0aChhcXVhcml1czo6Y29sb3JfZ2VuZSkpKQogIHJldHVybihnZW5lX3BhbGV0dGUpCn0KYGBgCgoKIyBBbGwgc2FtcGxlcwoKIyMgU2V0dGluZ3MKClRoaXMgaXMgdGhlIHByb2plY3Rpb24gbmFtZSB0byB2aXN1YWxpemUgY2VsbHMgOgoKYGBge3IgYWxsX3NvYmpfbmFtZTJEfQpuYW1lMkQgPSAiaGFybW9ueV8zOF90c25lIgpuYW1lMkRfYXRsYXMgPSBuYW1lMkQKYGBgCgojIyBQcmVwYXJhdGlvbgoKV2UgbWFrZSBhIGxvdyByZXNvbHV0aXZlIGNsdXN0ZXJpbmcgZm9yIHRoZSBoZWF0bWFwIDoKCmBgYHtyIGNsdXN0ZXJfYWxsfQpzb2JqID0gU2V1cmF0OjpGaW5kQ2x1c3RlcnMoc29iaiwgcmVzb2x1dGlvbiA9IDAuNCkKCmxlbmd0aChsZXZlbHMoc29iaiRzZXVyYXRfY2x1c3RlcnMpKQpgYGAKCgpXZSBkZWZpbmUgY2x1c3RlciB0eXBlIGFuZCBjbHVzdGVyIGZhbWlseSA6CgpgYGB7ciBjbHVzdGVyX3R5cGVfZmFtaWx5X2FsbCwgZmlnLndpZHRoID0gNywgZmlnLmhlaWdodCA9IDV9CnNvYmokY2VsbF90eXBlID0gc29iaiRjZWxsX3R5cGUgJT4lCiAgYXMuY2hhcmFjdGVyKCkgJT4lCiAgZmFjdG9yKC4sIGxldmVscyA9IG5hbWVzKGNvbG9yX21hcmtlcnMpKQoKY2x1c3Rlcl90eXBlID0gdGFibGUoc29iaiRjZWxsX3R5cGUsIHNvYmokc2V1cmF0X2NsdXN0ZXJzKSAlPiUKICBwcm9wLnRhYmxlKC4sIG1hcmdpbiA9IDIpICU+JQogIGFwcGx5KC4sIDIsIHdoaWNoLm1heCkKY2x1c3Rlcl90eXBlID0gc2V0TmFtZXMobm0gPSBuYW1lcyhjbHVzdGVyX3R5cGUpLAogICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMoc29iaiRjZWxsX3R5cGUpW2NsdXN0ZXJfdHlwZV0pCgpzb2JqJGNsdXN0ZXJfdHlwZSA9IGNsdXN0ZXJfdHlwZVtzb2JqJHNldXJhdF9jbHVzdGVyc10Kc29iaiRjbHVzdGVyX3R5cGUgPSBmYWN0b3Ioc29iaiRjbHVzdGVyX3R5cGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGxldmVscyhzb2JqJGNlbGxfdHlwZSkpCnNvYmokY2x1c3Rlcl9mYW1pbHkgPSBjdXN0b21fb3JkZXJfY2VsbF90eXBlW3NvYmokY2x1c3Rlcl90eXBlLCAiY2VsbF9mYW1pbHkiXQpzb2JqJGNsdXN0ZXJfZmFtaWx5ID0gZmFjdG9yKHNvYmokY2x1c3Rlcl9mYW1pbHksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gbmFtZXMoZmFtaWx5X2NvbG9yKSkKYGBgCgoKIyMgRmlndXJlcwoKUHJvamVjdCBuYW1lIDoKCmBgYHtyIGZpZzFfc2FtcGxlX2lkZW50aWZpZXIsIGZpZy53aWR0aCA9IDQsIGZpZy5oZWlnaHQgPSA0fQojIFJhbmRvbSBvcmRlcgpzZXQuc2VlZCgxMjM0KQpybmRfb3JkZXIgPSBzYW1wbGUoY29sbmFtZXMoc29iaiksIHJlcGxhY2UgPSBGQUxTRSwgc2l6ZSA9IG5jb2woc29iaikpCgojIEV4dHJhY3QgY29vcmRpbmF0ZXMKY2VsbHNfY29vcmQgPSBzb2JqQHJlZHVjdGlvbnNbW25hbWUyRF1dQGNlbGwuZW1iZWRkaW5ncyAlPiUKICBhcy5kYXRhLmZyYW1lKCkgJT4lCiAgYGNvbG5hbWVzPC1gKGMoIkRpbTEiLCAiRGltMiIpKQpjZWxsc19jb29yZCRwcm9qZWN0X25hbWUgPSBzb2JqJHByb2plY3RfbmFtZQpjZWxsc19jb29yZCA9IGNlbGxzX2Nvb3JkWyhybmRfb3JkZXIpLCBdCgojIFBsb3QKZ2dwbG90Mjo6Z2dwbG90KGNlbGxzX2Nvb3JkLCBhZXMoeCA9IERpbTEsIHkgPSBEaW0yLCBjb2wgPSBwcm9qZWN0X25hbWUpKSArCiAgZ2dwbG90Mjo6Z2VvbV9wb2ludChzaXplID0gMC41KSArCiAgZ2dwbG90Mjo6c2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IHNhbXBsZV9pbmZvJGNvbG9yLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBzYW1wbGVfaW5mbyRwcm9qZWN0X25hbWUpICsKICBnZ3Bsb3QyOjp0aGVtZV92b2lkKCkgKwogIGdncGxvdDI6OnRoZW1lKGFzcGVjdC5yYXRpbyA9IDEsCiAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQpgYGAKCgpTYW1wbGUgdHlwZSA6CgpgYGB7ciBmaWcxX3NhbXBsZV90eXBlLCBmaWcud2lkdGggPSA0LCBmaWcuaGVpZ2h0ID0gNH0KIyBFeHRyYWN0IGNvb3JkaW5hdGVzCmNlbGxzX2Nvb3JkID0gc29iakByZWR1Y3Rpb25zW1tuYW1lMkRdXUBjZWxsLmVtYmVkZGluZ3MgJT4lCiAgYXMuZGF0YS5mcmFtZSgpICU+JQogIGBjb2xuYW1lczwtYChjKCJEaW0xIiwgIkRpbTIiKSkKY2VsbHNfY29vcmQkc2FtcGxlX3R5cGUgPSBzb2JqJHNhbXBsZV90eXBlCmNlbGxzX2Nvb3JkID0gY2VsbHNfY29vcmRbKHJuZF9vcmRlciksIF0KCiMgUGxvdApnZ3Bsb3QyOjpnZ3Bsb3QoY2VsbHNfY29vcmQsIGFlcyh4ID0gRGltMSwgeSA9IERpbTIsIGNvbCA9IHNhbXBsZV90eXBlKSkgKwogIGdncGxvdDI6Omdlb21fcG9pbnQoc2l6ZSA9IDAuNSkgKwogIGdncGxvdDI6OnNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBzYW1wbGVfdHlwZV9jb2xvcnMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IG5hbWVzKHNhbXBsZV90eXBlX2NvbG9ycykpICsKICBnZ3Bsb3QyOjp0aGVtZV92b2lkKCkgKwogIGdncGxvdDI6OnRoZW1lKGFzcGVjdC5yYXRpbyA9IDEsCiAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQpgYGAKCkNsdXN0ZXIgOgoKYGBge3IgZmlnMV9jbHVzdGVyLCBmaWcud2lkdGggPSA0LCBmaWcuaGVpZ2h0ID0gNH0KZ3JleV9wYWxldHRlID0gc2V0TmFtZXMobm0gPSBsZXZlbHMoc29iaiRzZXVyYXRfY2x1c3RlcnMpLAogICAgICAgICAgICAgICAgICAgICAgICByZXAoIiNEOUQ5RDkiLCBsZW5ndGgobGV2ZWxzKHNvYmokc2V1cmF0X2NsdXN0ZXJzKSkpKQpncmV5X3BhbGV0dGVbYygiNyIsICIxNiIsICIxIiwgIjEyIiwgIjExIiwgIjEwIiwgIjE1IildID0gIiNCREJEQkQiCmdyZXlfcGFsZXR0ZVtjKCIxNiIsICIxNCIsICI1IiwgIjkiKV0gPSAiIzk2OTY5NiIKClNldXJhdDo6RGltUGxvdChzb2JqLCByZWR1Y3Rpb24gPSBuYW1lMkQsIHB0LnNpemUgPSAwLjQsCiAgICAgICAgICAgICAgICBncm91cC5ieSA9ICJzZXVyYXRfY2x1c3RlcnMiLCBjb2xzID0gZ3JleV9wYWxldHRlLAogICAgICAgICAgICAgICAgbGFiZWwgPSBUUlVFLCBsYWJlbC5zaXplID0gNikgKwogIGdncGxvdDI6OnRoZW1lKGFzcGVjdC5yYXRpbyA9IDEpICsKICBTZXVyYXQ6Ok5vQXhlcygpICsKICBTZXVyYXQ6Ok5vTGVnZW5kKCkKYGBgCgpDZWxsIHR5cGUgYW5ub3RhdGlvbiA6CgpgYGB7ciBmaWcxX2NlbGxfdHlwZSwgZmlnLndpZHRoID0gNCwgZmlnLmhlaWdodCA9IDR9ClNldXJhdDo6RGltUGxvdChzb2JqLCByZWR1Y3Rpb24gPSBuYW1lMkQsIHB0LnNpemUgPSAwLjUsCiAgICAgICAgICAgICAgICBncm91cC5ieSA9ICJjZWxsX3R5cGUiLCBjb2xzID0gY29sb3JfbWFya2VycykgKwogIGdncGxvdDI6OnRoZW1lKGFzcGVjdC5yYXRpbyA9IDEpICsKICBTZXVyYXQ6Ok5vQXhlcygpICsKICBTZXVyYXQ6Ok5vTGVnZW5kKCkKYGBgCgpDbHVzdGVyIGZhbWlseSBhbm5vdGF0aW9uIDoKCmBgYHtyIGZpZzFfY2x1c3Rlcl9mYW1pbHksIGZpZy53aWR0aCA9IDYsIGZpZy5oZWlnaHQgPSA2fQpTZXVyYXQ6OkRpbVBsb3Qoc29iaiwgcmVkdWN0aW9uID0gbmFtZTJELCBwdC5zaXplID0gMC41LAogICAgICAgICAgICAgICAgZ3JvdXAuYnkgPSAiY2x1c3Rlcl9mYW1pbHkiLCBjb2xzID0gZmFtaWx5X2NvbG9yKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXNwZWN0LnJhdGlvID0gMSkgKwogIFNldXJhdDo6Tm9BeGVzKCkgKwogIFNldXJhdDo6Tm9MZWdlbmQoKQpgYGAKCkNlbGwgdHlwZSBhbm5vdGF0aW9uIHNwbGl0IGJ5IGNvbmRpdGlvbiA6CgpgYGB7ciBmaWcxX2NlbGxfdHlwZV9zcGxpdCwgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDV9CnBsb3RfbGlzdCA9IGFxdWFyaXVzOjpwbG90X3NwbGl0X2RpbXJlZChzb2JqLCByZWR1Y3Rpb24gPSBuYW1lMkQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cF9ieSA9ICJjZWxsX3R5cGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXBfY29sb3IgPSBjb2xvcl9tYXJrZXJzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3BsaXRfYnkgPSAic2FtcGxlX3R5cGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3BsaXRfY29sb3IgPSBzYW1wbGVfdHlwZV9jb2xvcnMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBiZ19jb2xvciA9IGJnX2NvbG9yKQoKcGF0Y2h3b3JrOjp3cmFwX3Bsb3RzKHBsb3RfbGlzdCkgJgogIFNldXJhdDo6Tm9MZWdlbmQoKQpgYGAKCkdlbmUgZXhwcmVzc2lvbiB0byBhc3Nlc3MgYW5ub3RhdGlvbiA6CgpgYGB7ciBmaWcxX2dlbmVfZmFtaWx5LCBmaWcud2lkdGggPSA0LCBmaWcuaGVpZ2h0ID0gNH0KZ2VuZXMgPSBjKCJQVFBSQyIsICJNU1gyIiwgIktSVDE0IikKbmFtZXMoZ2VuZXMpID0gYygiaW1tdW5lIGNlbGxzIiwgIm1hdHJpeCBjZWxscyIsICJub24tbWF0cml4IGNlbGxzIikKCnBsb3RfbGlzdCA9IGxhcHBseShjKDE6bGVuZ3RoKGdlbmVzKSksIEZVTiA9IGZ1bmN0aW9uKGdlbmVfaWQpIHsKICBnZW5lID0gZ2VuZXNbW2dlbmVfaWRdXQogIHBvcCA9IG5hbWVzKGdlbmVzKVtnZW5lX2lkXQogIAogIHNvYmokbXlfZ2VuZSA9IFNldXJhdDo6RmV0Y2hEYXRhKHNvYmosIGdlbmUpWywgMV0gJT4lCiAgICBhcXVhcml1czo6cnVuX3Jlc2NhbGUoLiwgbmV3X21pbiA9IDAsIG5ld19tYXggPSAxMCkKICAKICBTZXVyYXQ6OkZlYXR1cmVQbG90KHNvYmosIGZlYXR1cmVzID0gIm15X2dlbmUiLCByZWR1Y3Rpb24gPSBuYW1lMkQpICsKICAgIGdncGxvdDI6OnNjYWxlX2NvbG9yX2dyYWRpZW50bihjb2xvcnMgPSBhcXVhcml1czo6Y29sb3JfZ2VuZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBzZXEoMCwgMTAsIGJ5ID0gMi41KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJtaW4iLCByZXAoIiIsIDMpLCAibWF4IikpICsKICAgIGdncGxvdDI6OmxhYnModGl0bGUgPSBnZW5lKSArIAogICAgIyBzdWJ0aXRsZSA9IHBvcCkgKwogICAgZ2dwbG90Mjo6dGhlbWUoYXNwZWN0LnJhdGlvID0gMSwKICAgICAgICAgICAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIHNpemUgPSAxNyksCiAgICAgICAgICAgICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBzaXplID0gMTUpLAogICAgICAgICAgICAgICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE1KSwKICAgICAgICAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwogICAgU2V1cmF0OjpOb0F4ZXMoKQp9KQoKcGxvdF9saXN0CmBgYAoKQmFycGxvdCBieSBjbHVzdGVyIGZhbWlseSA6CgpgYGB7ciBmaWcxX2JhcnBsb3RfZmFtaWx5LCBmaWcud2lkdGggPSAzLjUsIGZpZy5oZWlnaHQgPSAzLjV9CnF1YW50aWYgPSB0YWJsZShzb2JqJHNhbXBsZV9pZGVudGlmaWVyKSAlPiUKICBhcy5kYXRhLmZyYW1lLnRhYmxlKCkgJT4lCiAgYGNvbG5hbWVzPC1gKGMoIlNhbXBsZSIsICJuYl9jZWxscyIpKQoKYXF1YXJpdXM6OnBsb3RfYmFycGxvdChkZiA9IHRhYmxlKHNvYmokc2FtcGxlX2lkZW50aWZpZXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzb2JqJGNsdXN0ZXJfZmFtaWx5KSAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgIGFzLmRhdGEuZnJhbWUudGFibGUoKSAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgIGBjb2xuYW1lczwtYChjKCJTYW1wbGUiLCAiQ2VsbCBUeXBlIiwgIk51bWJlciIpKSwKICAgICAgICAgICAgICAgICAgICAgICB4ID0gIlNhbXBsZSIsIHkgPSAiTnVtYmVyIiwgZmlsbCA9ICJDZWxsIFR5cGUiLAogICAgICAgICAgICAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25fZmlsbCgpKSArCiAgZ2dwbG90Mjo6Z2VvbV9sYWJlbChkYXRhID0gcXVhbnRpZiwgaW5oZXJpdC5hZXMgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgIGFlcyh4ID0gLmRhdGEkU2FtcGxlLCB5ID0gMS4wNSwgbGFiZWwgPSAuZGF0YSRuYl9jZWxscyksCiAgICAgICAgICAgICAgICAgICAgICBsYWJlbC5zaXplID0gMCwgc2l6ZSA9IDQpICsKICBnZ3Bsb3QyOjpzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSB1bmxpc3QoZmFtaWx5X2NvbG9yKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBuYW1lcyhmYW1pbHlfY29sb3IpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSAiQ2VsbCBGYW1pbHkiKSArCiAgZ2dwbG90Mjo6c2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCAxLCBieSA9IDAuMjUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBwYXN0ZTAoc2VxKDAsIDEwMCwgYnkgPSAyNSksIHNlcCA9ICIgJSIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBleHBhbmQgPSBnZ3Bsb3QyOjpleHBhbnNpb24oYWRkID0gYygwLCAwLjA1KSkpICsKICBnZ3Bsb3QyOjp0aGVtZShheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgYXhpcy5saW5lLnggPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImxpZ2h0Z3JheSIpLAogICAgICAgICAgICAgICAgIHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE1KSwKICAgICAgICAgICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChtYXJnaW4gPSBtYXJnaW4odCA9IDI1LCByID0gMCwgYiA9IDAsIGwgPSAwKSksCiAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQpgYGAKCkhlYXRtYXAgb2YgY2x1c3RlciBwcm9wb3J0aW9uIGJ5IHNhbXBsZSA6CgpgYGB7ciBmaWcxX2hlYXRtYXBfcHJvcCwgZmlnLndpZHRoID0gOSwgZmlnLmhlaWdodCA9IDUsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUifQpncm91cF9ieSA9ICJzZXVyYXRfY2x1c3RlcnMiCgpjbHVzdGVyX2J5X3NhbXBsZSA9IHRhYmxlKHNvYmokc2FtcGxlX2lkZW50aWZpZXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgc29iakBtZXRhLmRhdGFbLCBncm91cF9ieV0pICU+JQogIHByb3AudGFibGUobWFyZ2luID0gMSkgJT4lCiAgYXMubWF0cml4KCkKCiMjIFJpZ2h0IGFubm90YXRpb24gOiBudW1iZXIgb2YgY2VsbHMgYnkgZGF0YXNldApodF9hbm5vdCA9IHRhYmxlKHNvYmokc2FtcGxlX2lkZW50aWZpZXIpICU+JQogIGFzLmRhdGEuZnJhbWUudGFibGUoKSAlPiUKICBgY29sbmFtZXM8LWAoYygic2FtcGxlX2lkZW50aWZpZXIiLCAibmJfY2VsbHMiKSkgJT4lCiAgYHJvd25hbWVzPC1gKC4kc2FtcGxlX2lkZW50aWZpZXIpICU+JQogIGRwbHlyOjpzZWxlY3QoLXNhbXBsZV9pZGVudGlmaWVyKQoKaGFfcmlnaHQgPSBDb21wbGV4SGVhdG1hcDo6SGVhdG1hcEFubm90YXRpb24oCiAgZGYgPSBodF9hbm5vdCwKICB3aGljaCA9ICJyb3ciLAogIHNob3dfbGVnZW5kID0gVFJVRSwKICBhbm5vdGF0aW9uX25hbWVfc2lkZSA9ICJ0b3AiLAogIGNvbCA9IGxpc3QobmJfY2VsbHMgID0gY2lyY2xpemU6OmNvbG9yUmFtcDIoY29sb3JzID0gUkNvbG9yQnJld2VyOjpicmV3ZXIucGFsKG5hbWUgPSAiR3JleXMiLCBuID0gOSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBzZXEoZnJvbSA9IHJhbmdlKGh0X2Fubm90JG5iX2NlbGxzKVsxXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0byA9IHJhbmdlKGh0X2Fubm90JG5iX2NlbGxzKVsyXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZW5ndGgub3V0ID0gOSkpKSkKCiMjIExlZnQgYW5ub3RhdGlvbiA6IGdlbmRlcgpoYV9sZWZ0ID0gQ29tcGxleEhlYXRtYXA6OkhlYXRtYXBBbm5vdGF0aW9uKAogIGdlbmRlciA9IHNhbXBsZV9pbmZvJGdlbmRlciwKICB3aGljaCA9ICJyb3ciLAogIHNob3dfbGVnZW5kID0gVFJVRSwKICBhbm5vdGF0aW9uX25hbWVfc2lkZSA9ICJ0b3AiLAogIGNvbCA9IGxpc3QoZ2VuZGVyID0gc2V0TmFtZXMobm0gPSBjKCJGIiwgIk0iKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGMoImxpZ2h0Y3lhbjMiLCAibmF2eWJsdWUiKSkpKQoKIyMgVG9wIGFubm90YXRpb24gOiBtYWluIGNlbGwgdHlwZSBpbiB0aGlzIGNsdXN0ZXIKaHRfYW5ub3QgPSB0YWJsZShzb2JqJHNhbXBsZV9pZGVudGlmaWVyLAogICAgICAgICAgICAgICAgIHNvYmpAbWV0YS5kYXRhWywgZ3JvdXBfYnldKSAlPiUKICBwcm9wLnRhYmxlKG1hcmdpbiA9IDEpICU+JQogIGFzLm1hdHJpeCgpCgpodF9hbm5vdCA9IHRhYmxlKHNvYmokY2VsbF90eXBlLAogICAgICAgICAgICAgICAgIHNvYmpAbWV0YS5kYXRhWywgZ3JvdXBfYnldKSAlPiUKICBwcm9wLnRhYmxlKC4sIG1hcmdpbiA9IDIpICU+JQogIGFwcGx5KC4sIDIsIHdoaWNoLm1heCkKaHRfYW5ub3QgPSBkYXRhLmZyYW1lKHJvdy5uYW1lcyA9IG5hbWVzKGh0X2Fubm90KSwKICAgICAgICAgICAgICAgICAgICAgIGNlbGxfdHlwZSA9IG5hbWVzKGNvbG9yX21hcmtlcnMpW2h0X2Fubm90XSwKICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkKaHRfYW5ub3QgPSBkcGx5cjo6bGVmdF9qb2luKGh0X2Fubm90LCBjdXN0b21fb3JkZXJfY2VsbF90eXBlLCBieSA9ICJjZWxsX3R5cGUiKSAlPiUKICAjIFNpbXBsaWZpY2F0aW9uIGZvciBtYXRyaXgKICBkcGx5cjo6bXV0YXRlKGNlbGxfdHlwZSA9IGlmZWxzZShjZWxsX3R5cGUgJWluJSBjKCJtZWR1bGxhIiwgImNvcnRleCIsICJjdXRpY2xlIiksIHllcyA9ICJoYWlyIHNoYWZ0Iiwgbm8gPSBjZWxsX3R5cGUpKSAlPiUKICAjIFNpbXBsaWZpY2F0aW9uIGZvciBUIGNlbGxzCiAgZHBseXI6Om11dGF0ZShjZWxsX3R5cGUgPSBpZmVsc2UoY2VsbF90eXBlICVpbiUgYygiQ0Q0IFQgY2VsbHMiLCAiQ0Q4IFQgY2VsbHMiKSwgeWVzID0gIlQgY2VsbHMiLCBubyA9IGNlbGxfdHlwZSkpICU+JQogICMgU2ltcGxpZmljYXRpb24gZm9yIEFQQwogIGRwbHlyOjptdXRhdGUoY2VsbF90eXBlID0gaWZlbHNlKGNlbGxfdHlwZSAlaW4lIGMoIkxhbmdlcmhhbnMgY2VsbHMiLCAibWFjcm9waGFnZXMiKSwgeWVzID0gIkFQQyIsIG5vID0gY2VsbF90eXBlKSkgJT4lCiAgIyBBZGQgY29sb3IKICBkcGx5cjo6bXV0YXRlKGNvbG9yID0gYXMuY2hhcmFjdGVyKGNvbG9yX21hcmtlcnNbY2VsbF90eXBlXSkpICU+JQogIGRwbHlyOjptdXRhdGUoY29sb3IgPSBpZmVsc2UoY2VsbF90eXBlID09ICJoYWlyIHNoYWZ0IiwgeWVzID0gIiNGRkI2QzEiLCBubyA9IGNvbG9yKSkgJT4lCiAgZHBseXI6Om11dGF0ZShjb2xvciA9IGlmZWxzZShjZWxsX3R5cGUgPT0gIlQgY2VsbHMiLCB5ZXMgPSAiIzhBNkVFNiIsIG5vID0gY29sb3IpKSAlPiUKICBkcGx5cjo6bXV0YXRlKGNvbG9yID0gaWZlbHNlKGNlbGxfdHlwZSA9PSAiQVBDIiwgeWVzID0gIiM5Q0FBNEIiLCBubyA9IGNvbG9yKSkKCmhhX3RvcCA9IENvbXBsZXhIZWF0bWFwOjpIZWF0bWFwQW5ub3RhdGlvbigKICAjIGNlbGxfdHlwZSA9IGh0X2Fubm90JGNlbGxfdHlwZSwKICBjZWxsX2ZhbWlseSA9IGh0X2Fubm90JGNlbGxfZmFtaWx5LAogIHdoaWNoID0gImNvbHVtbiIsCiAgc2hvd19sZWdlbmQgPSBUUlVFLAogIHNob3dfYW5ub3RhdGlvbl9uYW1lID0gRkFMU0UsCiAgIyBhbm5vdGF0aW9uX25hbWVfc2lkZSA9ICJsZWZ0IiwKICBjb2wgPSBsaXN0KCNjZWxsX3R5cGUgPSBzZXROYW1lcyhubSA9IGh0X2Fubm90JGNlbGxfdHlwZSwKICAgICMgICAgICAgICAgICAgICAgICAgICAgaHRfYW5ub3QkY29sb3IpLAogICAgY2VsbF9mYW1pbHkgPSBmYW1pbHlfY29sb3IKICApKQoKIyMgQXNzZW1ibGUgaGVhdG1hcApodCA9IENvbXBsZXhIZWF0bWFwOjpIZWF0bWFwKGNsdXN0ZXJfYnlfc2FtcGxlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhlYXRtYXBfbGVnZW5kX3BhcmFtID0gbGlzdCh0aXRsZSA9ICJQcm9wb3J0aW9uIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sID0gYygiIzIxNjZBQyIsICIjRjdGN0Y3IiwgIiNCMjE4MkIiKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBib3R0b21fYW5ub3RhdGlvbiA9IGhhX2JvdHRvbSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICByaWdodF9hbm5vdGF0aW9uID0gaGFfcmlnaHQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVmdF9hbm5vdGF0aW9uID0gaGFfbGVmdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0b3BfYW5ub3RhdGlvbiA9IGhhX3RvcCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjbHVzdGVyX3Jvd3MgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsdXN0ZXJfY29sdW1ucyA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcm93X3RpdGxlID0gIlNhbXBsZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcm93X25hbWVzX2dwID0gZ3JpZDo6Z3BhcihuYW1lcyA9IHNhbXBsZV9pbmZvJHNhbXBsZV9pZGVudGlmaWVyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sID0gc2FtcGxlX2luZm8kY29sb3IsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmb250ZmFjZSA9ICJib2xkIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sdW1uX3RpdGxlID0gIkNsdXN0ZXIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbHVtbl9uYW1lc19jZW50ZXJlZCA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcm93X25hbWVzX3NpZGUgPSAibGVmdCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sdW1uX25hbWVzX3NpZGUgPSAidG9wIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2x1bW5fbmFtZXNfcm90ID0gMCkKCiMjIERyYXcgIQpDb21wbGV4SGVhdG1hcDo6ZHJhdyhodCwgbWVyZ2VfbGVnZW5kcyA9IFRSVUUpCmBgYAoKRm9yIHRoZSBkb3RwbG90LCB3ZSBjbGFyaWZ5IGNsdXN0ZXJzIGFuZCBjZWxsIHR5cGUgYW5ub3RhdGlvbiA6CgpgYGB7ciBpbXByb3ZlX2N1c3RvbV9vcmRlcl9jZWxsX3R5cGV9CmNlbGxfdHlwZV9pbl9jbHVzdGVyID0gdGFibGUoc29iaiRjZWxsX3R5cGUsIHNvYmokc2V1cmF0X2NsdXN0ZXJzKSAlPiUKICBwcm9wLnRhYmxlKC4sIG1hcmdpbiA9IDEpICU+JQogIGFwcGx5KC4sIDEsIHdoaWNoLm1heCkKY2VsbF90eXBlX2luX2NsdXN0ZXIgPSBjZWxsX3R5cGVfaW5fY2x1c3RlciAtIDEKCm1pc3NpbmdfY2x1c3RlciA9IHNldGRpZmYobGV2ZWxzKHNvYmokc2V1cmF0X2NsdXN0ZXJzKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBjZWxsX3R5cGVfaW5fY2x1c3RlcikKCmNlbGxfdHlwZV9pbl9jbHVzdGVyID0gZGF0YS5mcmFtZShjZWxsX3R5cGUgPSBjKG5hbWVzKGNlbGxfdHlwZV9pbl9jbHVzdGVyKSwgY2x1c3Rlcl90eXBlW21pc3NpbmdfY2x1c3Rlcl0pLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2x1c3Rlcl9pZCA9IGMoY2VsbF90eXBlX2luX2NsdXN0ZXIsIG5hbWVzKGNsdXN0ZXJfdHlwZVttaXNzaW5nX2NsdXN0ZXJdKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UsIHJvdy5uYW1lcyA9IE5VTEwpICU+JQogIGRwbHlyOjptdXRhdGUoY2x1c3Rlcl9pZCA9IGFzLm51bWVyaWMoY2x1c3Rlcl9pZCkpICU+JQogIGRwbHlyOjphcnJhbmdlKGNlbGxfdHlwZSwgY2x1c3Rlcl9pZCkKCmN1c3RvbV9vcmRlcl9jZWxsX3R5cGUkY2x1c3RlcnMgPSBjdXN0b21fb3JkZXJfY2VsbF90eXBlICU+JQogIGFwcGx5KC4sIE1BUkdJTiA9IDEsIEZVTiA9IGZ1bmN0aW9uKG9uZV9yb3cpIHsKICAgIGNlbGxfdHlwZSA9IG9uZV9yb3dbImNlbGxfdHlwZSJdCiAgICBjbHVzdGVycyA9IGNlbGxfdHlwZV9pbl9jbHVzdGVyICU+JQogICAgICBkcGx5cjo6ZmlsdGVyKC5kYXRhJGNlbGxfdHlwZSA9PSAuZW52JGNlbGxfdHlwZSkgJT4lCiAgICAgIGRwbHlyOjpwdWxsKGNsdXN0ZXJfaWQpCiAgICAKICAgIGNlbGxfdHlwZV9jbHVzdGVyID0gcGFzdGUwKGNlbGxfdHlwZSwgIiAoIiwgcGFzdGUwKGNsdXN0ZXJzLCBjb2xsYXBzZSA9ICIsICIpLCAiKSIpCiAgICAKICAgIHJldHVybihjZWxsX3R5cGVfY2x1c3RlcikKICB9KSAlPiUKICBmYWN0b3IoLiwgbGV2ZWxzID0gLikKCmN1c3RvbV9vcmRlcl9jZWxsX3R5cGUKYGBgCgpEb3RwbG90IDoKCmBgYHtyIGZpZzFfZG90cGxvdCwgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDguNSwgY2xhc3Muc291cmNlID0gImZvbGQtaGlkZSJ9CnBsb3RfbGlzdCA9IGFxdWFyaXVzOjpwbG90X2RvdHBsb3Qoc29iaiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXJrZXJzID0gYygiUFRQUkMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJDRDNFIiwgIkNENCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNEM0UiLCAiQ0Q4QSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNEMjA3IiwgIkFJRjEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJUUkVNMiIsICJNU1IxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ0Q3OUEiLCAiQ0Q3OUIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgIlBSRE0xIiwgIktSVDg1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTVNYMiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIktSVDMyIiwgIktSVDM1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiS1JUMzEiLCAiUFJSOSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkJBTUJJIiwgIkFMREgxQTMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJLUlQ3MSIsICJLUlQ3MyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlRPUDJBIiwgIk1DTTUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJLUlQxNCIsICJDWENMMTQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJLUlQxNSIsICJDT0wxN0ExIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRElPMiIsICJUQ0VBTDIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJLUlQxNiIsICJLUlQ2QyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlNQSU5LNSIsICJMWTZEIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ0xNUCIsICJQUEFSRyIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFzc2F5ID0gIlJOQSIsIGNvbHVtbl9uYW1lID0gImNlbGxfdHlwZSIsIG5iX2hsaW5lID0gMCkgKwogIGdncGxvdDI6OnNjYWxlX2NvbG9yX2dyYWRpZW50bihjb2xvcnMgPSBhcXVhcml1czo6Y29sb3JfZ2VuZSkgKwogIGdncGxvdDI6OnRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJsZWZ0IiwKICAgICAgICAgICAgICAgICBsZWdlbmQuanVzdGlmaWNhdGlvbiA9ICJib3R0b20iLAogICAgICAgICAgICAgICAgIGxlZ2VuZC5ib3ggPSAidmVydGljYWwiLAogICAgICAgICAgICAgICAgIGxlZ2VuZC5ib3gubWFyZ2luID0gbWFyZ2luKDAsNzAsMCwwKSwKICAgICAgICAgICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgIGF4aXMudGlja3MueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICBheGlzLmxpbmUueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICBwbG90Lm1hcmdpbiA9IHVuaXQocmVwKDAsIDQpLCAiY20iKSkKCnAgPSBnZ3Bsb3QyOjpnZ3Bsb3QoY3VzdG9tX29yZGVyX2NlbGxfdHlwZSwgYWVzKHggPSBjbHVzdGVycywgeSA9IDApKSArCiAgZ2dwbG90Mjo6Z2VvbV9wb2ludChzaXplID0gMCkgKwogIGdncGxvdDI6Omdlb21fc2VnbWVudChhZXMoeCA9IDAuNSwgeGVuZCA9IDUuNSwgeSA9IDAsIHllbmQgPSAwKSwgc2l6ZSA9IDYsIGNvbCA9IGZhbWlseV9jb2xvclsiaW1tdW5lIGNlbGxzIl0pICsKICBnZ3Bsb3QyOjpnZW9tX3NlZ21lbnQoYWVzKHggPSA1LjUsIHhlbmQgPSAxMC41LCB5ID0gMCwgeWVuZCA9IDApLCBzaXplID0gNiwgY29sID0gZmFtaWx5X2NvbG9yWyJtYXRyaXgiXSkgKwogIGdncGxvdDI6Omdlb21fc2VnbWVudChhZXMoeCA9IDEwLjUsIHhlbmQgPSAxNS41LCB5ID0gMCwgeWVuZCA9IDApLCBzaXplID0gNiwgY29sID0gZmFtaWx5X2NvbG9yWyJub24gbWF0cml4Il0pICsKICBnZ3Bsb3QyOjpzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gYygwLDApLCBsaW1pdHMgPSBjKDAsMCkpICsKICBnZ3Bsb3QyOjp0aGVtZV9jbGFzc2ljKCkgKwogIGdncGxvdDI6OnRoZW1lKGF4aXMudGV4dC55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgIGF4aXMudGlja3MueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgIGF4aXMubGluZS55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSwgc2l6ZSA9IDEwLCBjb2xvciA9ICJibGFjayIpLAogICAgICAgICAgICAgICAgIHBsb3QubWFyZ2luID0gdW5pdChjKDAsMC41LDAuNSwwKSwgImNtIikpCgpwbG90X2xpc3QgPSBwYXRjaHdvcms6OndyYXBfcGxvdHMocGxvdF9saXN0LCBwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmNvbCA9IDEsIGhlaWdodHMgPSBjKDI1LCAxKSkKcGxvdF9saXN0CmBgYAoKCiMgSW1tdW5lIGNlbGxzCgojIyBTZXR0aW5ncwoKV2UgbG9hZCB0aGUgaW1tdW5lIGNlbGxzIGRhdGFzZXQgOgoKYGBge3Igc29ial9pY30Kc29ial9pYyA9IHJlYWRSRFMocGFzdGUwKGRhdGFfZGlyLCAiLzRfem9vbS8xX3pvb21faW1tdW5lL2ltbXVuZV9jZWxsc19zb2JqLnJkcyIpKQpzb2JqX2ljCmBgYAoKClRoaXMgaXMgdGhlIHByb2plY3Rpb24gbmFtZSB0byB2aXN1YWxpemUgY2VsbHMgOgoKYGBge3Igc29ial9uYW1lMkRfaWN9Cm5hbWUyRCA9ICJoYXJtb255XzIwX3RzbmUiCmBgYAoKVG8gcmVwcmVzZW50IHJlc3VsdHMgZnJvbSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiwgd2UgbG9hZCB0aGUgYW5hbHlzZXMgcmVzdWx0cyA6CgpgYGB7ciBsaXN0X3Jlc3VsdHNfaWN9Cmxpc3RfcmVzdWx0cyA9IHJlYWRSRFMocGFzdGUwKGRhdGFfZGlyLCAiLzRfem9vbS8xX3pvb21faW1tdW5lL2ltbXVuZV9jZWxsc19saXN0X3Jlc3VsdHMucmRzIikpCgpsYXBwbHkobGlzdF9yZXN1bHRzLCBGVU4gPSBuYW1lcykKYGBgCgoKIyMgUHJlcGFyYXRpb24KCldlIGRlZmluZWQgY2x1c3RlciB0eXBlIGFuZCBjbHVzdGVyIGZhbWlseSA6CgpgYGB7ciBjbHVzdGVyX3R5cGVfZmFtaWx5X2ljLCBmaWcud2lkdGggPSA3LCBmaWcuaGVpZ2h0ID0gNX0KY2x1c3Rlcl90eXBlID0gdGFibGUoc29ial9pYyRjZWxsX3R5cGUsIHNvYmpfaWMkc2V1cmF0X2NsdXN0ZXJzKSAlPiUKICBwcm9wLnRhYmxlKC4sIG1hcmdpbiA9IDIpICU+JQogIGFwcGx5KC4sIDIsIHdoaWNoLm1heCkKY2x1c3Rlcl90eXBlID0gc2V0TmFtZXMobm0gPSBuYW1lcyhjbHVzdGVyX3R5cGUpLAogICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMoc29ial9pYyRjZWxsX3R5cGUpW2NsdXN0ZXJfdHlwZV0pCgpzb2JqX2ljJGNsdXN0ZXJfdHlwZSA9IGNsdXN0ZXJfdHlwZVtzb2JqX2ljJHNldXJhdF9jbHVzdGVyc10Kc29ial9pYyRjbHVzdGVyX3R5cGUgPSBmYWN0b3Ioc29ial9pYyRjbHVzdGVyX3R5cGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGxldmVscyhzb2JqX2ljJGNlbGxfdHlwZSkpCnNvYmpfaWMkY2x1c3Rlcl9mYW1pbHkgPSBjdXN0b21fb3JkZXJfY2VsbF90eXBlW3NvYmpfaWMkY2x1c3Rlcl90eXBlLCAiY2VsbF9mYW1pbHkiXQpzb2JqX2ljJGNsdXN0ZXJfZmFtaWx5ID0gZmFjdG9yKHNvYmpfaWMkY2x1c3Rlcl9mYW1pbHksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gbmFtZXMoZmFtaWx5X2NvbG9yKSkKYGBgCgoKIyMgRmlndXJlcwoKQ29udHJvbCBjZWxscyBvbiB0aGUgZnVsbCBhdGxhcyA6CgpgYGB7ciBmaWdfaWNfbG9jYXRpb24sIGZpZy53aWR0aCA9IDIsIGZpZy5oZWlnaHQgPSAyfQpzb2JqJGlzX2ltbXVuZSA9IChjb2xuYW1lcyhzb2JqKSAlaW4lIGNvbG5hbWVzKHNvYmpfaWMpKQoKU2V1cmF0OjpEaW1QbG90KHNvYmosIHJlZHVjdGlvbiA9IG5hbWUyRF9hdGxhcywgcHQuc2l6ZSA9IDAuMDAwMDAxLAogICAgICAgICAgICAgICAgZ3JvdXAuYnkgPSAiaXNfaW1tdW5lIiwgb3JkZXIgPSAiVFJVRSIpICsKICBnZ3Bsb3QyOjpzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYyhmYW1pbHlfY29sb3JbWyJpbW11bmUgY2VsbHMiXV0sIGJnX2NvbG9yKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gYyhUUlVFLCBGQUxTRSkpICsKICBnZ3Bsb3QyOjpsYWJzKHRpdGxlID0gIkltbXVuZSBjZWxscyIsCiAgICAgICAgICAgICAgICBzdWJ0aXRsZSA9IHBhc3RlMChuY29sKHNvYmpfaWMpLCAiIGNlbGxzIikpICsKICBnZ3Bsb3QyOjp0aGVtZShhc3BlY3QucmF0aW8gPSAxLAogICAgICAgICAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIpLAogICAgICAgICAgICAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKSArCiAgU2V1cmF0OjpOb0F4ZXMoKSArIFNldXJhdDo6Tm9MZWdlbmQoKQpgYGAKCgpWaW9saW4gcGxvdCBvZiBJTDFCIGluIG1hY3JvcGhhZ2VzIDoKCmBgYHtyIGZpZ19pY19tYWNfaWwxYiwgZmlnLndpZHRoID0gMiwgZmlnLmhlaWdodCA9IDIuNX0Kc3Vic29iaiA9IHN1YnNldChzb2JqX2ljLCBzZXVyYXRfY2x1c3RlcnMgPT0gMikKdGFibGUoc3Vic29iaiRzYW1wbGVfdHlwZSkKCmlsMWJfaHMgPSBzdWJzb2JqQGFzc2F5cyRSTkFAZGF0YVsiSUwxQiIsIHN1YnNvYmokc2FtcGxlX3R5cGUgPT0gIkhTIl0KaWwxYl9oZCA9IHN1YnNvYmpAYXNzYXlzJFJOQUBkYXRhWyJJTDFCIiwgc3Vic29iaiRzYW1wbGVfdHlwZSA9PSAiSEQiXQppbDFiX2hzX1ZTX2lsMWJfaGQgPSBzdGF0czo6dC50ZXN0KGlsMWJfaHMsIGlsMWJfaGQpCmlsMWJfaHNfVlNfaWwxYl9oZAoKU2V1cmF0OjpWbG5QbG90KHN1YnNvYmosIGdyb3VwLmJ5ID0gInNhbXBsZV90eXBlIiwgcHQuc2l6ZSA9IDAuMywKICAgICAgICAgICAgICAgIGZlYXR1cmVzID0gIklMMUIiLCBjb2xzID0gc2FtcGxlX3R5cGVfY29sb3JzKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKYGBgCgpTcGxpdCBieSBzYW1wbGUgOgoKYGBge3IgZmlnX2ljX21hY19pbDFiX3NwbGl0LCBmaWcud2lkdGggPSA2LCBmaWcuaGVpZ2h0ID0gNH0KU2V1cmF0OjpWbG5QbG90KHN1YnNvYmosIGdyb3VwLmJ5ID0gInNhbXBsZV9pZGVudGlmaWVyIiwKICAgICAgICAgICAgICAgIGZlYXR1cmVzID0gIklMMUIiLCBjb2xzID0gc2FtcGxlX2luZm8kY29sb3IpICsKICBnZ3Bsb3QyOjp0aGVtZShheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQpgYGAKClZpb2xpbiBwbG90IG9mIElMMUIgaW4gbWFjcm9waGFnZXMgOgoKYGBge3IgZmlnX2ljX21hY19pbDYsIGZpZy53aWR0aCA9IDIsIGZpZy5oZWlnaHQgPSAyLjV9CmlsNl9ocyA9IHN1YnNvYmpAYXNzYXlzJFJOQUBkYXRhWyJJTDYiLCBzdWJzb2JqJHNhbXBsZV90eXBlID09ICJIUyJdCmlsNl9oZCA9IHN1YnNvYmpAYXNzYXlzJFJOQUBkYXRhWyJJTDYiLCBzdWJzb2JqJHNhbXBsZV90eXBlID09ICJIRCJdCmlsNl9oc19WU19pbDZfaGQgPSBzdGF0czo6dC50ZXN0KGlsNl9ocywgaWw2X2hkKQppbDZfaHNfVlNfaWw2X2hkCgpTZXVyYXQ6OlZsblBsb3Qoc3Vic29iaiwgZ3JvdXAuYnkgPSAic2FtcGxlX3R5cGUiLCBwdC5zaXplID0gMC4zLAogICAgICAgICAgICAgICAgZmVhdHVyZXMgPSAiSUw2IiwgY29scyA9IHNhbXBsZV90eXBlX2NvbG9ycykgKwogIGdncGxvdDI6OnRoZW1lKGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCmBgYAoKVmlvbGluIHBsb3Qgb2YgVE5GIGluIG1hY3JvcGhhZ2VzIDoKCmBgYHtyIGZpZ19pY19tYWNfdG5mLCBmaWcud2lkdGggPSAyLCBmaWcuaGVpZ2h0ID0gMi41fQp0bmZfaHMgPSBzdWJzb2JqQGFzc2F5cyRSTkFAZGF0YVsiVE5GIiwgc3Vic29iaiRzYW1wbGVfdHlwZSA9PSAiSFMiXQp0bmZfaGQgPSBzdWJzb2JqQGFzc2F5cyRSTkFAZGF0YVsiVE5GIiwgc3Vic29iaiRzYW1wbGVfdHlwZSA9PSAiSEQiXQp0bmZfaHNfVlNfdG5mX2hkID0gc3RhdHM6OnQudGVzdCh0bmZfaHMsIHRuZl9oZCkKdG5mX2hzX1ZTX3RuZl9oZAoKU2V1cmF0OjpWbG5QbG90KHN1YnNvYmosIGdyb3VwLmJ5ID0gInNhbXBsZV90eXBlIiwgcHQuc2l6ZSA9IDAuMywKICAgICAgICAgICAgICAgIGZlYXR1cmVzID0gIlRORiIsIGNvbHMgPSBzYW1wbGVfdHlwZV9jb2xvcnMpICsKICBnZ3Bsb3QyOjp0aGVtZShheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQpgYGAKClNwbGl0IGJ5IHNhbXBsZSA6CgpgYGB7ciBmaWdfaWNfbWFjX3RuZl9zcGxpdCwgZmlnLndpZHRoID0gNiwgZmlnLmhlaWdodCA9IDR9ClNldXJhdDo6VmxuUGxvdChzdWJzb2JqLCBncm91cC5ieSA9ICJzYW1wbGVfaWRlbnRpZmllciIsCiAgICAgICAgICAgICAgICBmZWF0dXJlcyA9ICJUTkYiLCBjb2xzID0gc2FtcGxlX2luZm8kY29sb3IpICsKICBnZ3Bsb3QyOjp0aGVtZShheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQpgYGAKCgpWaW9saW4gcGxvdCBvZiBHWk1BIGluIENENCBUIGNlbGxzIDoKCmBgYHtyIGZpZ19pY190NF9nem1hLCBmaWcud2lkdGggPSAyLCBmaWcuaGVpZ2h0ID0gMi41fQpzdWJzb2JqID0gc3Vic2V0KHNvYmpfaWMsIHNldXJhdF9jbHVzdGVycyAlaW4lIGMoMCwxMCkpCnRhYmxlKHN1YnNvYmokc2FtcGxlX3R5cGUpCgpnem1hX2hzID0gc3Vic29iakBhc3NheXMkUk5BQGRhdGFbIkdaTUEiLCBzdWJzb2JqJHNhbXBsZV90eXBlID09ICJIUyJdCmd6bWFfaGQgPSBzdWJzb2JqQGFzc2F5cyRSTkFAZGF0YVsiR1pNQSIsIHN1YnNvYmokc2FtcGxlX3R5cGUgPT0gIkhEIl0KZ3ptYV9oc19WU19nem1hX2hkID0gc3RhdHM6OnQudGVzdChnem1hX2hzLCBnem1hX2hkKQpnem1hX2hzX1ZTX2d6bWFfaGQKClNldXJhdDo6VmxuUGxvdChzdWJzb2JqLCBncm91cC5ieSA9ICJzYW1wbGVfdHlwZSIsIHB0LnNpemUgPSAwLjMsCiAgICAgICAgICAgICAgICBmZWF0dXJlcyA9ICJHWk1BIiwgY29scyA9IHNhbXBsZV90eXBlX2NvbG9ycykgKwogIGdncGxvdDI6OnRoZW1lKGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCmBgYAoKVmlvbGluIHBsb3Qgb2YgSUZORyBpbiBDRDQgVCBjZWxscyA6CgpgYGB7ciBmaWdfaWNfdDRfaWZuZywgZmlnLndpZHRoID0gMiwgZmlnLmhlaWdodCA9IDIuNX0KaWZuZ19ocyA9IHN1YnNvYmpAYXNzYXlzJFJOQUBkYXRhWyJJRk5HIiwgc3Vic29iaiRzYW1wbGVfdHlwZSA9PSAiSFMiXQppZm5nX2hkID0gc3Vic29iakBhc3NheXMkUk5BQGRhdGFbIklGTkciLCBzdWJzb2JqJHNhbXBsZV90eXBlID09ICJIRCJdCmlmbmdfaHNfVlNfaWZuZ19oZCA9IHN0YXRzOjp0LnRlc3QoaWZuZ19ocywgaWZuZ19oZCkKaWZuZ19oc19WU19pZm5nX2hkCgpTZXVyYXQ6OlZsblBsb3Qoc3Vic29iaiwgZ3JvdXAuYnkgPSAic2FtcGxlX3R5cGUiLCBwdC5zaXplID0gMC4zLAogICAgICAgICAgICAgICAgZmVhdHVyZXMgPSAiSUZORyIsIGNvbHMgPSBzYW1wbGVfdHlwZV9jb2xvcnMpICsKICBnZ3Bsb3QyOjp0aGVtZShheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQpgYGAKVmlvbGluIHBsb3Qgb2YgSUwxN0EgaW4gQ0Q0IFQgY2VsbHMgOgoKYGBge3IgZmlnX2ljX3Q0X0lMMTcsIGZpZy53aWR0aCA9IDIsIGZpZy5oZWlnaHQgPSAyLjV9CklMMTdfaHMgPSBzdWJzb2JqQGFzc2F5cyRSTkFAZGF0YVsiSUwxN0EiLCBzdWJzb2JqJHNhbXBsZV90eXBlID09ICJIUyJdCklMMTdfaGQgPSBzdWJzb2JqQGFzc2F5cyRSTkFAZGF0YVsiSUwxN0EiLCBzdWJzb2JqJHNhbXBsZV90eXBlID09ICJIRCJdCklMMTdfaHNfVlNfSUwxN19oZCA9IHN0YXRzOjp0LnRlc3QoSUwxN19ocywgSUwxN19oZCkKSUwxN19oc19WU19JTDE3X2hkCgpTZXVyYXQ6OlZsblBsb3Qoc3Vic29iaiwgZ3JvdXAuYnkgPSAic2FtcGxlX3R5cGUiLCBwdC5zaXplID0gMC4zLAogICAgICAgICAgICAgICAgZmVhdHVyZXMgPSAiSUwxN1JFIiwgY29scyA9IHNhbXBsZV90eXBlX2NvbG9ycykgKwogIGdncGxvdDI6OnRoZW1lKGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCmBgYAoKV2UgcmVwcmVzZW50IHNvbWUgZ2VuZXMgc3BsaXQgYnkgc2FtcGxlIHR5cGUgOgoKYGBge3IgZmlnX2ljX2dlbmVfc3BsaXQsIGZpZy53aWR0aCA9IDYsIGZpZy5oZWlnaHQgPSAzfQpwbG90X2xpc3QgPSBsYXBwbHkoYygiSUwxQiIsICJHWk1BIiwgIklGTkciLCAiSUwxN0EiLCAiVE5GIiwgIklMNiIpLCBGVU4gPSBmdW5jdGlvbihvbmVfZ2VuZSkgewogIHAgPSBhcXVhcml1czo6cGxvdF9zcGxpdF9kaW1yZWQoc29ial9pYywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlZHVjdGlvbiA9IG5hbWUyRCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNwbGl0X2J5ID0gInNhbXBsZV90eXBlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yX2J5ID0gb25lX2dlbmUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvcl9wYWxldHRlID0gYygiZ3JheTcwIiwgIiNGREJCODQiLCAiI0VGNjU0OCIsICIjN0YwMDAwIiwgImJsYWNrIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYWluX3B0X3NpemUgPSAwLjYsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBiZ19wdF9zaXplID0gMC42LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3JkZXIgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYmdfY29sb3IgPSAiZ3JheTk1IikKICBwID0gcGF0Y2h3b3JrOjp3cmFwX3Bsb3RzKHAsIG5yb3cgPSAxKSArCiAgICBwYXRjaHdvcms6OnBsb3RfbGF5b3V0KGd1aWRlcyA9ICJjb2xsZWN0IikgKwogICAgZ2dwbG90Mjo6dGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikgJgogICAgZ2dwbG90Mjo6dGhlbWUocGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkKICByZXR1cm4ocCkKfSkKCnBsb3RfbGlzdApgYGAKCkJhcnBsb3QgYnkgY2x1c3RlciB0eXBlIDoKCmBgYHtyIGZpZ19pY19iYXJwbG90LCBmaWcud2lkdGggPSA0LCBmaWcuaGVpZ2h0ID0gNH0KcXVhbnRpZiA9IHRhYmxlKHNvYmpfaWMkc2FtcGxlX2lkZW50aWZpZXIpICU+JQogIGFzLmRhdGEuZnJhbWUudGFibGUoKSAlPiUKICBgY29sbmFtZXM8LWAoYygiU2FtcGxlIiwgIm5iX2NlbGxzIikpCgphcXVhcml1czo6cGxvdF9iYXJwbG90KGRmID0gdGFibGUoc29ial9pYyRzYW1wbGVfaWRlbnRpZmllciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNvYmpfaWMkY2x1c3Rlcl90eXBlKSAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgIGFzLmRhdGEuZnJhbWUudGFibGUoKSAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgIGBjb2xuYW1lczwtYChjKCJTYW1wbGUiLCAiQ2VsbCBUeXBlIiwgIk51bWJlciIpKSwKICAgICAgICAgICAgICAgICAgICAgICB4ID0gIlNhbXBsZSIsIHkgPSAiTnVtYmVyIiwgZmlsbCA9ICJDZWxsIFR5cGUiLAogICAgICAgICAgICAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25fc3RhY2soKSkgKwogIGdncGxvdDI6Omdlb21fbGFiZWwoZGF0YSA9IHF1YW50aWYsIGluaGVyaXQuYWVzID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICBhZXMoeCA9IC5kYXRhJFNhbXBsZSwgeSA9IDUwICsgLmRhdGEkbmJfY2VsbHMsIGxhYmVsID0gLmRhdGEkbmJfY2VsbHMpLAogICAgICAgICAgICAgICAgICAgICAgbGFiZWwuc2l6ZSA9IDAsIHNpemUgPSA1KSArCiAgZ2dwbG90Mjo6c2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gdW5saXN0KGNvbG9yX21hcmtlcnMpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IG5hbWVzKGNvbG9yX21hcmtlcnMpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSAiQ2VsbCBUeXBlIikgKwogIGdncGxvdDI6OnNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKDAsIDEwMCArIG1heChxdWFudGlmJG5iX2NlbGxzKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV4cGFuZCA9IGdncGxvdDI6OmV4cGFuc2lvbihhZGQgPSBjKDAsIDAuMDUpKSkgKwogIGdncGxvdDI6OnRoZW1lKGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICBheGlzLmxpbmUueCA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAibGlnaHRncmF5IiksCiAgICAgICAgICAgICAgICAgdGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUpLAogICAgICAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKYGBgCgpIZWF0bWFwIGZvciBtYWNyb3BoYWdlcyA6CgpgYGB7ciBmaWdfaWNfaGVhdG1hcF9tYWNyb3BoYWdlcywgZmlnLndpZHRoID0gNSwgZmlnLmhlaWdodCA9IDUuMiwgY2xhc3Muc291cmNlID0gImZvbGQtaGlkZSJ9CnN1YnNvYmogPSBzdWJzZXQoc29ial9pYywgY2x1c3Rlcl90eXBlID09ICJtYWNyb3BoYWdlcyIpCmZlYXR1cmVzX29pID0gYygiSUwxQiIsICJUTkYiLAogICAgICAgICAgICAgICAgIkhMQS1EUUEyIiwgIkhMQS1EUEExIiwgIkhMQS1EUkI1IiwKICAgICAgICAgICAgICAgICJITEEtQSIsICJITEEtQyIsICJCMk0iLAogICAgICAgICAgICAgICAgIkMxUUEiLCAiQzFRQiIsICJDMVFDIikKCiMgTWF0cml4Cm1hdF9leHByID0gU2V1cmF0OjpHZXRBc3NheURhdGEoc3Vic29iaikKbWF0X2V4cHIgPSBtYXRfZXhwcltmZWF0dXJlc19vaSwgXQptYXRfZXhwciA9IE1hdHJpeDo6dChtYXRfZXhwcikKbWF0X2V4cHIgPSBkeW51dGlsczo6c2NhbGVfcXVhbnRpbGUobWF0X2V4cHIpICMgYmV0d2VlbiAwIGFuZCAxCm1hdF9leHByID0gTWF0cml4Ojp0KG1hdF9leHByKQpkaW0obWF0X2V4cHIpICMgZ2VuZXMgeCBjZWxscwojIyBDb2xvcnMKbGlzdF9jb2xvcnMgPSBsaXN0KCkKCiMgSGVhdG1hcApsaXN0X2NvbG9yc1tbImV4cHJlc3Npb24iXV0gPSByZXYoUkNvbG9yQnJld2VyOjpicmV3ZXIucGFsKG5hbWUgPSAiUmRCdSIsIG4gPSA5KSkKCiMgU2FtcGxlIGFubm90YXRpb24gKHRvcCBhbm5vdGF0aW9uKQpsaXN0X2NvbG9yc1tbInNhbXBsZV90eXBlIl1dID0gc2FtcGxlX3R5cGVfY29sb3JzCmxpc3RfY29sb3JzW1sic2FtcGxlX2lkZW50aWZpZXIiXV0gPSBzZXROYW1lcyhubSA9IHNhbXBsZV9pbmZvJHNhbXBsZV9pZGVudGlmaWVyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2FtcGxlX2luZm8kY29sb3IpCiMgQ2VsbHMgb3JkZXIKY29sdW1uX29yZGVyID0gc3Vic29iakBtZXRhLmRhdGEgJT4lCiAgZHBseXI6OmFycmFuZ2Uoc2FtcGxlX3R5cGUsIHNhbXBsZV9pZGVudGlmaWVyKSAlPiUKICByb3duYW1lcygpCmNvbHVtbl9vcmRlciA9IG1hdGNoKGNvbHVtbl9vcmRlciwgcm93bmFtZXMoc3Vic29iakBtZXRhLmRhdGEpKQoKIyBIZWF0bWFwCmhhX3RvcCA9IEhlYXRtYXBBbm5vdGF0aW9uKHNhbXBsZV90eXBlID0gc3Vic29iaiRzYW1wbGVfdHlwZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgc2FtcGxlX2lkZW50aWZpZXIgPSBzdWJzb2JqJHNhbXBsZV9pZGVudGlmaWVyLAogICAgICAgICAgICAgICAgICAgICAgICAgICBjb2wgPSBsaXN0KHNhbXBsZV90eXBlID0gbGlzdF9jb2xvcnNbWyJzYW1wbGVfdHlwZSJdXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzYW1wbGVfaWRlbnRpZmllciA9IGxpc3RfY29sb3JzW1sic2FtcGxlX2lkZW50aWZpZXIiXV0pKQoKIyBIZWF0bWFwCmh0ID0gSGVhdG1hcChhcy5tYXRyaXgobWF0X2V4cHIpLAogICAgICAgICAgICAgaGVhdG1hcF9sZWdlbmRfcGFyYW0gPSBsaXN0KHRpdGxlID0gIkV4cHJlc3Npb24iLCBhdCA9IGMoMCwgMSksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoImxvdyIsICJoaWdoIikpLAogICAgICAgICAgICAgY29sID0gbGlzdF9jb2xvcnNbWyJleHByZXNzaW9uIl1dLAogICAgICAgICAgICAgdG9wX2Fubm90YXRpb24gPSBoYV90b3AsCiAgICAgICAgICAgICBzaG93X2NvbHVtbl9uYW1lcyA9IEZBTFNFLAogICAgICAgICAgICAgY29sdW1uX29yZGVyID0gY29sdW1uX29yZGVyLAogICAgICAgICAgICAgY29sdW1uX2dhcCA9IHVuaXQoMiwgIm1tIiksCiAgICAgICAgICAgICBjbHVzdGVyX3Jvd3MgPSBGQUxTRSwKICAgICAgICAgICAgIHJvd190aXRsZSA9IE5VTEwsCiAgICAgICAgICAgICByb3dfbmFtZXNfZ3AgPSBncmlkOjpncGFyKGZvbnRzaXplID0gMTQsIGZvbnRmYWNlID0gInBsYWluIiksCiAgICAgICAgICAgICB1c2VfcmFzdGVyID0gRkFMU0UsCiAgICAgICAgICAgICBzaG93X2hlYXRtYXBfbGVnZW5kID0gVFJVRSwKICAgICAgICAgICAgIGJvcmRlciA9IFRSVUUpCgpDb21wbGV4SGVhdG1hcDo6ZHJhdyhodCwKICAgICAgICAgICAgICAgICAgICAgbWVyZ2VfbGVnZW5kID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgaGVhdG1hcF9sZWdlbmRfc2lkZSA9ICJib3R0b20iLAogICAgICAgICAgICAgICAgICAgICBhbm5vdGF0aW9uX2xlZ2VuZF9zaWRlID0gImJvdHRvbSIpCmBgYAoKSGVhdG1hcCBmb3IgQ0Q0IFQgY2VsbHMgOgoKYGBge3IgZmlnX2ljX2hlYXRtYXBfY2Q0LCBmaWcud2lkdGggPSA2LCBmaWcuaGVpZ2h0ID0gNCwgY2xhc3Muc291cmNlID0gImZvbGQtaGlkZSJ9CnN1YnNvYmogPSBzdWJzZXQoc29ial9pYywgY2x1c3Rlcl90eXBlID09ICJDRDQgVCBjZWxscyIpCmZlYXR1cmVzX29pID0gYygiR1pNQSIsICJLTFJCMSIsICJCVEcxIiwgIlpGUDM2IiwgIk5GS0JJQSIsICJUWE5JUCIsICJDWENSNCIsICJJRk5HIiwgIklMMTdBIikKCiMgTWF0cml4Cm1hdF9leHByID0gU2V1cmF0OjpHZXRBc3NheURhdGEoc3Vic29iaikKbWF0X2V4cHIgPSBtYXRfZXhwcltmZWF0dXJlc19vaSwgXQptYXRfZXhwciA9IE1hdHJpeDo6dChtYXRfZXhwcikKbWF0X2V4cHIgPSBkeW51dGlsczo6c2NhbGVfcXVhbnRpbGUobWF0X2V4cHIpICMgYmV0d2VlbiAwIGFuZCAxCm1hdF9leHByID0gTWF0cml4Ojp0KG1hdF9leHByKQpkaW0obWF0X2V4cHIpICMgZ2VuZXMgeCBjZWxscwojIyBDb2xvcnMKbGlzdF9jb2xvcnMgPSBsaXN0KCkKCiMgSGVhdG1hcApsaXN0X2NvbG9yc1tbImV4cHJlc3Npb24iXV0gPSByZXYoUkNvbG9yQnJld2VyOjpicmV3ZXIucGFsKG5hbWUgPSAiUmRCdSIsIG4gPSA5KSkKCiMgU2FtcGxlIGFubm90YXRpb24gKHRvcCBhbm5vdGF0aW9uKQpsaXN0X2NvbG9yc1tbInNhbXBsZV90eXBlIl1dID0gc2FtcGxlX3R5cGVfY29sb3JzCmxpc3RfY29sb3JzW1sic2FtcGxlX2lkZW50aWZpZXIiXV0gPSBzZXROYW1lcyhubSA9IHNhbXBsZV9pbmZvJHNhbXBsZV9pZGVudGlmaWVyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2FtcGxlX2luZm8kY29sb3IpCiMgQ2VsbHMgb3JkZXIKY29sdW1uX29yZGVyID0gc3Vic29iakBtZXRhLmRhdGEgJT4lCiAgZHBseXI6OmFycmFuZ2Uoc2FtcGxlX3R5cGUsIHNhbXBsZV9pZGVudGlmaWVyKSAlPiUKICByb3duYW1lcygpCmNvbHVtbl9vcmRlciA9IG1hdGNoKGNvbHVtbl9vcmRlciwgcm93bmFtZXMoc3Vic29iakBtZXRhLmRhdGEpKQoKIyBIZWF0bWFwCmhhX3RvcCA9IEhlYXRtYXBBbm5vdGF0aW9uKHNhbXBsZV90eXBlID0gc3Vic29iaiRzYW1wbGVfdHlwZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgc2FtcGxlX2lkZW50aWZpZXIgPSBzdWJzb2JqJHNhbXBsZV9pZGVudGlmaWVyLAogICAgICAgICAgICAgICAgICAgICAgICAgICBjb2wgPSBsaXN0KHNhbXBsZV90eXBlID0gbGlzdF9jb2xvcnNbWyJzYW1wbGVfdHlwZSJdXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzYW1wbGVfaWRlbnRpZmllciA9IGxpc3RfY29sb3JzW1sic2FtcGxlX2lkZW50aWZpZXIiXV0pKQoKIyBIZWF0bWFwCmh0ID0gSGVhdG1hcChhcy5tYXRyaXgobWF0X2V4cHIpLAogICAgICAgICAgICAgaGVhdG1hcF9sZWdlbmRfcGFyYW0gPSBsaXN0KHRpdGxlID0gIkV4cHJlc3Npb24iLCBhdCA9IGMoMCwgMSksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoImxvdyIsICJoaWdoIikpLAogICAgICAgICAgICAgY29sID0gbGlzdF9jb2xvcnNbWyJleHByZXNzaW9uIl1dLAogICAgICAgICAgICAgdG9wX2Fubm90YXRpb24gPSBoYV90b3AsCiAgICAgICAgICAgICBzaG93X2NvbHVtbl9uYW1lcyA9IEZBTFNFLAogICAgICAgICAgICAgY29sdW1uX29yZGVyID0gY29sdW1uX29yZGVyLAogICAgICAgICAgICAgY29sdW1uX2dhcCA9IHVuaXQoMiwgIm1tIiksCiAgICAgICAgICAgICBjbHVzdGVyX3Jvd3MgPSBGQUxTRSwKICAgICAgICAgICAgIHJvd190aXRsZSA9IE5VTEwsCiAgICAgICAgICAgICByb3dfbmFtZXNfZ3AgPSBncmlkOjpncGFyKGZvbnRzaXplID0gMTQsIGZvbnRmYWNlID0gInBsYWluIiksCiAgICAgICAgICAgICB1c2VfcmFzdGVyID0gRkFMU0UsCiAgICAgICAgICAgICBzaG93X2hlYXRtYXBfbGVnZW5kID0gVFJVRSwKICAgICAgICAgICAgIGJvcmRlciA9IFRSVUUpCgpDb21wbGV4SGVhdG1hcDo6ZHJhdyhodCwKICAgICAgICAgICAgICAgICAgICAgbWVyZ2VfbGVnZW5kID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgaGVhdG1hcF9sZWdlbmRfc2lkZSA9ICJsZWZ0IiwKICAgICAgICAgICAgICAgICAgICAgYW5ub3RhdGlvbl9sZWdlbmRfc2lkZSA9ICJsZWZ0IikKYGBgCgoKIyBIRlNDCgojIyBTZXR0aW5ncwoKV2UgbG9hZCB0aGUgSEZTQ3MgZGF0YXNldCA6CgpgYGB7ciBzb2JqX2hmc2N9CnNvYmpfaGZzYyA9IHJlYWRSRFMocGFzdGUwKGRhdGFfZGlyLCAiLzRfem9vbS8yX3pvb21faGZzYy9oZnNjX3NvYmoucmRzIikpCnNvYmpfaGZzYwpgYGAKCgpUaGlzIGlzIHRoZSBwcm9qZWN0aW9uIG5hbWUgdG8gdmlzdWFsaXplIGNlbGxzIDoKCmBgYHtyIHNvYmpfbmFtZTJEX2hmc2N9Cm5hbWUyRCA9ICJoYXJtb255XzI0X3RzbmUiCmBgYAoKVG8gcmVwcmVzZW50IHJlc3VsdHMgZnJvbSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiwgd2UgbG9hZCB0aGUgYW5hbHlzZXMgcmVzdWx0cyA6CgpgYGB7ciBsaXN0X3Jlc3VsdHNfaGZzY30KbGlzdF9yZXN1bHRzID0gcmVhZFJEUyhwYXN0ZTAoZGF0YV9kaXIsICIvNF96b29tLzJfem9vbV9oZnNjL2hmc2NfbGlzdF9yZXN1bHRzLnJkcyIpKQoKbGFwcGx5KGxpc3RfcmVzdWx0cywgRlVOID0gbmFtZXMpCmBgYAoKIyMgRmlndXJlcwoKSEZTQ3Mgb24gdGhlIGZ1bGwgYXRsYXMgOgoKYGBge3IgZmlnX2hmc2NfbG9jYXRpb24sIGZpZy53aWR0aCA9IDIsIGZpZy5oZWlnaHQgPSAyfQpzb2JqJGlzX2hmc2MgPSAoY29sbmFtZXMoc29iaikgJWluJSBjb2xuYW1lcyhzb2JqX2hmc2MpKQoKU2V1cmF0OjpEaW1QbG90KHNvYmosIHJlZHVjdGlvbiA9IG5hbWUyRF9hdGxhcywgcHQuc2l6ZSA9IDAuMDAwMDAxLAogICAgICAgICAgICAgICAgZ3JvdXAuYnkgPSAiaXNfaGZzYyIsIG9yZGVyID0gIlRSVUUiKSArCiAgZ2dwbG90Mjo6c2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoY29sb3JfbWFya2Vyc1tbIkhGU0MiXV0sIGJnX2NvbG9yKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gYyhUUlVFLCBGQUxTRSkpICsKICBnZ3Bsb3QyOjpsYWJzKHRpdGxlID0gIkhGU0NzIiwKICAgICAgICAgICAgICAgIHN1YnRpdGxlID0gcGFzdGUwKG5jb2woc29ial9oZnNjKSwgIiBjZWxscyIpKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXNwZWN0LnJhdGlvID0gMSwKICAgICAgICAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBmYWNlID0gImJvbGQiKSwKICAgICAgICAgICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkgKwogIFNldXJhdDo6Tm9BeGVzKCkgKyBTZXVyYXQ6Ok5vTGVnZW5kKCkKYGBgCgpLUlQxNSBleHByZXNzaW9uIDoKCmBgYHtyIGZpZ19oZnNjX2tydDE1LCBmaWcud2lkdGggPSAyLCBmaWcuaGVpZ2h0ID0gMn0KU2V1cmF0OjpGZWF0dXJlUGxvdChzb2JqLCByZWR1Y3Rpb24gPSBuYW1lMkRfYXRsYXMsIHB0LnNpemUgPSAwLjAwMDAwMSwKICAgICAgICAgICAgICAgICAgICBmZWF0dXJlcyA9ICJLUlQxNSIpICsKICBnZ3Bsb3QyOjpzY2FsZV9jb2xvcl9ncmFkaWVudG4oY29sb3JzID0gYXF1YXJpdXM6OmNvbG9yX2dlbmUpICsKICBnZ3Bsb3QyOjp0aGVtZShhc3BlY3QucmF0aW8gPSAxKSArCiAgU2V1cmF0OjpOb0F4ZXMoKSArIFNldXJhdDo6Tm9MZWdlbmQoKQpgYGAKCkdlbmVzIG9mIGludGVyZXN0IDoKCmBgYHtyIGZpZ19oZnNjX2dlbmVzLCBmaWcud2lkdGggPSA0LCBmaWcuaGVpZ2h0ID0gNH0KZ2VuZXMgPSBjKCJUR0ZCMiIsICJBTkdQVEw3IiwgIkZHRjE4IiwgIk1HUCIsICJFUENBTSIsICJLUlQ3NSIsICJOT1RDSDMiLCAiUFRITEgiKQoKcGxvdF9saXN0ID0gbGFwcGx5KGMoMTpsZW5ndGgoZ2VuZXMpKSwgRlVOID0gZnVuY3Rpb24oZ2VuZV9pZCkgewogIGdlbmUgPSBnZW5lc1tbZ2VuZV9pZF1dCiAgCiAgc29ial9oZnNjJG15X2dlbmUgPSBTZXVyYXQ6OkZldGNoRGF0YShzb2JqX2hmc2MsIGdlbmUpWywgMV0gJT4lCiAgICBhcXVhcml1czo6cnVuX3Jlc2NhbGUoLiwgbmV3X21pbiA9IDAsIG5ld19tYXggPSAxMCkKICAKICBTZXVyYXQ6OkZlYXR1cmVQbG90KHNvYmpfaGZzYywgZmVhdHVyZXMgPSAibXlfZ2VuZSIsIHJlZHVjdGlvbiA9IG5hbWUyRCkgKwogICAgZ2dwbG90Mjo6c2NhbGVfY29sb3JfZ3JhZGllbnRuKGNvbG9ycyA9IGFxdWFyaXVzOjpjb2xvcl9nZW5lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IHNlcSgwLCAxMCwgYnkgPSAyLjUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIm1pbiIsIHJlcCgiIiwgMyksICJtYXgiKSkgKwogICAgZ2dwbG90Mjo6bGFicyh0aXRsZSA9IGdlbmUpICsKICAgIGdncGxvdDI6OnRoZW1lKGFzcGVjdC5yYXRpbyA9IDEsCiAgICAgICAgICAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBzaXplID0gMTcpLAogICAgICAgICAgICAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgc2l6ZSA9IDE1KSwKICAgICAgICAgICAgICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNSksCiAgICAgICAgICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsKICAgIFNldXJhdDo6Tm9BeGVzKCkKfSkKCnBsb3RfbGlzdApgYGAKClByb2plY3QgbmFtZSA6CgpgYGB7ciBmaWdfaGZzY19wcm9qZWN0X25hbWUsIGZpZy53aWR0aCA9IDQsIGZpZy5oZWlnaHQgPSA0fQojIFJhbmRvbSBvcmRlcgpzZXQuc2VlZCgxMjM0KQpybmRfb3JkZXIgPSBzYW1wbGUoY29sbmFtZXMoc29ial9oZnNjKSwgcmVwbGFjZSA9IEZBTFNFLCBzaXplID0gbmNvbChzb2JqX2hmc2MpKQoKIyBFeHRyYWN0IGNvb3JkaW5hdGVzCmNlbGxzX2Nvb3JkID0gc29ial9oZnNjQHJlZHVjdGlvbnNbW25hbWUyRF1dQGNlbGwuZW1iZWRkaW5ncyAlPiUKICBhcy5kYXRhLmZyYW1lKCkgJT4lCiAgYGNvbG5hbWVzPC1gKGMoIkRpbTEiLCAiRGltMiIpKQpjZWxsc19jb29yZCRwcm9qZWN0X25hbWUgPSBzb2JqX2hmc2MkcHJvamVjdF9uYW1lCmNlbGxzX2Nvb3JkID0gY2VsbHNfY29vcmRbKHJuZF9vcmRlciksIF0KCiMgUGxvdApnZ3Bsb3QyOjpnZ3Bsb3QoY2VsbHNfY29vcmQsIGFlcyh4ID0gRGltMSwgeSA9IERpbTIsIGNvbCA9IHByb2plY3RfbmFtZSkpICsKICBnZ3Bsb3QyOjpnZW9tX3BvaW50KHNpemUgPSAxLjIpICsKICBnZ3Bsb3QyOjpzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gc2FtcGxlX2luZm8kY29sb3IsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IHNhbXBsZV9pbmZvJHByb2plY3RfbmFtZSkgKwogIGdncGxvdDI6OnRoZW1lX3ZvaWQoKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXNwZWN0LnJhdGlvID0gMSwKICAgICAgICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCmBgYAoKQ2x1c3RlciA6CgpgYGB7ciBmaWdfaGZzY19jbHVzdGVycywgZmlnLndpZHRoID0gNCwgZmlnLmhlaWdodCA9IDR9ClNldXJhdDo6RGltUGxvdChzb2JqX2hmc2MsIHJlZHVjdGlvbiA9IG5hbWUyRCwgcHQuc2l6ZSA9IDEsCiAgICAgICAgICAgICAgICBncm91cC5ieSA9ICJzZXVyYXRfY2x1c3RlcnMiLCBjb2xzID0gZ3JleV9wYWxldHRlLAogICAgICAgICAgICAgICAgbGFiZWwgPSBUUlVFLCBsYWJlbC5zaXplID0gNykgKwogIGdncGxvdDI6OnRoZW1lKGFzcGVjdC5yYXRpbyA9IDEpICsKICBTZXVyYXQ6Ok5vQXhlcygpICsKICBTZXVyYXQ6Ok5vTGVnZW5kKCkKYGBgCgpIZWF0bWFwIHdpdGggcHJvcG9ydGlvbnMgOgoKYGBge3IgZmlnX2hmc2NfaGVhdG1hcCwgZmlnLndpZHRoID0gNS41LCBmaWcuaGVpZ2h0ID0gNS41LCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1oaWRlIn0KY2x1c3Rlcl9tYXJrZXJzID0gYygiVEdGQjIiLCAiQU5HUFRMNyIsICJFUENBTSIsICJLUlQ3NSIsICJOT1RDSDMiLCAiUFRITEgiKQoKIyMgQm90dG9tIGFubm90YXRpb24gOiBnZW5lIGV4cHJlc3Npb24gYnkgY2x1c3RlcgpodF9hbm5vdCA9IFNldXJhdDo6RmV0Y2hEYXRhKHNvYmpfaGZzYywgc2xvdCA9ICJkYXRhIiwgdmFycyA9IGNsdXN0ZXJfbWFya2VycykgJT4lCiAgYXMuZGF0YS5mcmFtZSgpCmh0X2Fubm90JGNsdXN0ZXJzID0gc29ial9oZnNjJHNldXJhdF9jbHVzdGVycwpodF9hbm5vdCA9IGh0X2Fubm90ICU+JQogIGRwbHlyOjpncm91cF9ieShjbHVzdGVycykgJT4lCiAgZHBseXI6OnN1bW1hcmlzZV9hbGwoZnVucygnbWVhbicgPSBtZWFuKSkgJT4lCiAgYXMuZGF0YS5mcmFtZSgpICU+JQogIGRwbHlyOjpzZWxlY3QoLWNsdXN0ZXJzKSAlPiUKICBgY29sbmFtZXM8LWAoYyhjbHVzdGVyX21hcmtlcnMpKQoKaGFfYm90dG9tID0gQ29tcGxleEhlYXRtYXA6OkhlYXRtYXBBbm5vdGF0aW9uKGRmID0gaHRfYW5ub3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB3aGljaCA9ICJjb2x1bW4iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hvd19sZWdlbmQgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sID0gc2V0TmFtZXMobm0gPSBjbHVzdGVyX21hcmtlcnMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYXBwbHkoY2x1c3Rlcl9tYXJrZXJzLCBGVU4gPSBjb2xvcl9mdW4pKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFubm90YXRpb25fbmFtZV9zaWRlID0gImxlZnQiKQoKIyMgUmlnaHQgYW5ub3RhdGlvbiA6IG51bWJlciBvZiBjZWxscyBieSBkYXRhc2V0Cmh0X2Fubm90ID0gdGFibGUoc29ial9oZnNjJHNhbXBsZV9pZGVudGlmaWVyKSAlPiUKICBhcy5kYXRhLmZyYW1lLnRhYmxlKCkgJT4lCiAgYGNvbG5hbWVzPC1gKGMoInNhbXBsZV9pZGVudGlmaWVyIiwgIm5iX2NlbGxzIikpICU+JQogIGByb3duYW1lczwtYCguJHNhbXBsZV9pZGVudGlmaWVyKSAlPiUKICBkcGx5cjo6c2VsZWN0KC1zYW1wbGVfaWRlbnRpZmllcikKCmhhX3JpZ2h0ID0gQ29tcGxleEhlYXRtYXA6OkhlYXRtYXBBbm5vdGF0aW9uKAogIGRmID0gaHRfYW5ub3QsCiAgd2hpY2ggPSAicm93IiwKICBzaG93X2xlZ2VuZCA9IFRSVUUsCiAgYW5ub3RhdGlvbl9uYW1lX3NpZGUgPSAiYm90dG9tIiwKICBjb2wgPSBsaXN0KG5iX2NlbGxzICA9IGNpcmNsaXplOjpjb2xvclJhbXAyKGNvbG9ycyA9IFJDb2xvckJyZXdlcjo6YnJld2VyLnBhbChuYW1lID0gIkdyZXlzIiwgbiA9IDkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gc2VxKGZyb20gPSByYW5nZShodF9hbm5vdCRuYl9jZWxscylbMV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdG8gPSByYW5nZShodF9hbm5vdCRuYl9jZWxscylbMl0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVuZ3RoLm91dCA9IDkpKSkpCgojIyBIZWF0bWFwCmh0ID0gYXF1YXJpdXM6OnBsb3RfcHJvcF9oZWF0bWFwKGRmID0gc29ial9oZnNjQG1ldGEuZGF0YVssIGMoInNhbXBsZV9pZGVudGlmaWVyIiwgInNldXJhdF9jbHVzdGVycyIpXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYm90dG9tX2Fubm90YXRpb24gPSBoYV9ib3R0b20sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgcmlnaHRfYW5ub3RhdGlvbiA9IGhhX3JpZ2h0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjbHVzdGVyX3Jvd3MgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2x1bW5fbmFtZXNfY2VudGVyZWQgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcm9wX21hcmdpbiA9IDEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJvd19uYW1lc19ncCA9IGdyaWQ6OmdwYXIobmFtZXMgPSBzYW1wbGVfaW5mbyRzYW1wbGVfaWRlbnRpZmllciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2wgPSBzYW1wbGVfaW5mbyRjb2xvciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmb250ZmFjZSA9ICJib2xkIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJvd190aXRsZSA9ICJTYW1wbGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2x1bW5fdGl0bGUgPSAiQ2x1c3RlciIpCgpDb21wbGV4SGVhdG1hcDo6ZHJhdyhodCwKICAgICAgICAgICAgICAgICAgICAgbWVyZ2VfbGVnZW5kID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgaGVhdG1hcF9sZWdlbmRfc2lkZSA9ICJib3R0b20iKQpgYGAKCkhlYXRtYXAgZm9yIGNsdXN0ZXIgMCBhbmQgOCA6CgpgYGB7ciBmaWdfaGZzY19oZWF0bWFwX2NsdXN0ZXIwOCwgZmlnLndpZHRoID0gNS41LCBmaWcuaGVpZ2h0ID0gMTAsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUifQpzdWJzb2JqID0gc3Vic2V0KHNvYmpfaGZzYywgc2V1cmF0X2NsdXN0ZXJzICVpbiUgYygwLDgpKQoKZmVhdHVyZXNfb2kgPSByb3duYW1lcyhsaXN0X3Jlc3VsdHMkY2x1c3Rlcl8wXzgpCmZlYXR1cmVzX29pID0gZmVhdHVyZXNfb2lbIWdyZXBsKGZlYXR1cmVzX29pLCBwYXR0ZXJuID0gIl5SUCIpXQoKIyBNYXRyaXgKbWF0X2V4cHIgPSBTZXVyYXQ6OkdldEFzc2F5RGF0YShzdWJzb2JqKQptYXRfZXhwciA9IG1hdF9leHByW2ZlYXR1cmVzX29pLCBdCm1hdF9leHByID0gTWF0cml4Ojp0KG1hdF9leHByKQptYXRfZXhwciA9IGNiaW5kKG1hdF9leHByLCBzdWJzb2JqJHBlcmNlbnQubXQpCmNvbG5hbWVzKG1hdF9leHByKVtuY29sKG1hdF9leHByKV0gPSAicGVyY2VudC5yYiIKbWF0X2V4cHIgPSBkeW51dGlsczo6c2NhbGVfcXVhbnRpbGUobWF0X2V4cHIpICMgYmV0d2VlbiAwIGFuZCAxCm1hdF9leHByID0gTWF0cml4Ojp0KG1hdF9leHByKQpkaW0obWF0X2V4cHIpICMgZ2VuZXMgeCBjZWxscwojIyBDb2xvcnMKbGlzdF9jb2xvcnMgPSBsaXN0KCkKCiMgSGVhdG1hcApsaXN0X2NvbG9yc1tbImV4cHJlc3Npb24iXV0gPSByZXYoUkNvbG9yQnJld2VyOjpicmV3ZXIucGFsKG5hbWUgPSAiUmRCdSIsIG4gPSA5KSkKCiMgU2FtcGxlIGFubm90YXRpb24gKHRvcCBhbm5vdGF0aW9uKQpsaXN0X2NvbG9yc1tbInNhbXBsZV90eXBlIl1dID0gc2FtcGxlX3R5cGVfY29sb3JzCmxpc3RfY29sb3JzW1sic2FtcGxlX2lkZW50aWZpZXIiXV0gPSBzZXROYW1lcyhubSA9IHNhbXBsZV9pbmZvJHNhbXBsZV9pZGVudGlmaWVyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2FtcGxlX2luZm8kY29sb3IpCiMgbGlzdF9jb2xvcnNbWyJzZXVyYXRfY2x1c3RlcnMiXV0gPSBzZXROYW1lcyhhcXVhcml1czo6Z2dfY29sb3JfaHVlKGxlbmd0aChsZXZlbHMoc3Vic29iaiRzZXVyYXRfY2x1c3RlcnMpKSksCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBubSA9IGxldmVscyhzdWJzb2JqJHNldXJhdF9jbHVzdGVycykpCiMgQ2VsbHMgb3JkZXIKY29sdW1uX29yZGVyID0gc3Vic29iakBtZXRhLmRhdGEgJT4lCiAgZHBseXI6OmFycmFuZ2Uoc2FtcGxlX3R5cGUsIHNhbXBsZV9pZGVudGlmaWVyKSAlPiUKICByb3duYW1lcygpCmNvbHVtbl9vcmRlciA9IG1hdGNoKGNvbHVtbl9vcmRlciwgcm93bmFtZXMoc3Vic29iakBtZXRhLmRhdGEpKQoKIyBIZWF0bWFwCmhhX3RvcCA9IEhlYXRtYXBBbm5vdGF0aW9uKHNhbXBsZV90eXBlID0gc3Vic29iaiRzYW1wbGVfdHlwZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgc2FtcGxlX2lkZW50aWZpZXIgPSBzdWJzb2JqJHNhbXBsZV9pZGVudGlmaWVyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAjIGNsdXN0ZXJzID0gc3Vic29iaiRzZXVyYXRfY2x1c3RlcnMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbCA9IGxpc3Qoc2FtcGxlX3R5cGUgPSBsaXN0X2NvbG9yc1tbInNhbXBsZV90eXBlIl1dLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNhbXBsZV9pZGVudGlmaWVyID0gbGlzdF9jb2xvcnNbWyJzYW1wbGVfaWRlbnRpZmllciJdXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgY2x1c3RlcnMgPSBsaXN0X2NvbG9yc1tbInNldXJhdF9jbHVzdGVycyJdXQogICAgICAgICAgICAgICAgICAgICAgICAgICApKQoKCiMgZzEgOiBSRUFDVE9NRV9DWVRPS0lORV9TSUdOQUxJTkdfSU5fSU1NVU5FX1NZU1RFTQojIGcyIDogR09CUF9BUE9QVE9USUNfUFJPQ0VTUwpnMV9nZW5lcyA9IGMoIkIyTSIsICJITEEtQyIsICJITEEtQSIsICJNSUYiLCAiUFBJQSIsICJKVU5CIiwgIklGSVRNMyIpCmcyX2dlbmVzID0gYygiSnVuIiwgIkFURjMiLCAiQlRHMiIsICJSSE9CIiwgIk5GS0JJQSIsICJTR0sxIiwgIktMRjkiLAogICAgICAgICAgICAgIkNBVjEiLCAiRERJVDQiLCAiUERLNCIsICJUWE5JUCIsICJSTkYxMTUyIiwgIlRMRTEiKQpoYV9yaWdodCA9IGRhdGEuZnJhbWUoZ2VuZXMgPSAgYyhmZWF0dXJlc19vaSwgInBlcmNlbnQucmIiKSwgcm93bmFtZXMgPSBjKGZlYXR1cmVzX29pLCAicGVyY2VudC5yYiIpKQpoYV9yaWdodCRncm91cCA9IGNhc2Vfd2hlbihoYV9yaWdodCRnZW5lcyAlaW4lIGcxX2dlbmVzIH4gIlJFQUNUT01FX0NZVE9LSU5FX1NJR05BTElOR19JTl9JTU1VTkVfU1lTVEVNIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgaGFfcmlnaHQkZ2VuZXMgJWluJSBnMl9nZW5lcyB+ICJHT0JQX0FQT1BUT1RJQ19QUk9DRVNTIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgVFJVRSB+ICJvdGhlcnMiKQoKbGlzdF9jb2xvcnNbWyJncm91cCJdXSA9IHNldE5hbWVzKAogIG5tID0gYygiUkVBQ1RPTUVfQ1lUT0tJTkVfU0lHTkFMSU5HX0lOX0lNTVVORV9TWVNURU0iLCAiR09CUF9BUE9QVE9USUNfUFJPQ0VTUyIsICJvdGhlcnMiKSwKICBjKCJyZWQiLCAiYmxhY2siLCAiZ3JheTkwIikpCgpoYV9yaWdodCA9IEhlYXRtYXBBbm5vdGF0aW9uKGdyb3VwID0gaGFfcmlnaHQkZ3JvdXAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sID0gbGlzdChncm91cCA9IGxpc3RfY29sb3JzW1siZ3JvdXAiXV0pLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHdoaWNoID0gInJvdyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hvd19hbm5vdGF0aW9uX25hbWUgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaG93X2xlZ2VuZCA9IFRSVUUpCgojIEhlYXRtYXAKaHQgPSBIZWF0bWFwKGFzLm1hdHJpeChtYXRfZXhwciksCiAgICAgICAgICAgICBoZWF0bWFwX2xlZ2VuZF9wYXJhbSA9IGxpc3QodGl0bGUgPSAiRXhwcmVzc2lvbiIsIGF0ID0gYygwLCAxKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygibG93IiwgImhpZ2giKSksCiAgICAgICAgICAgICBjb2wgPSBsaXN0X2NvbG9yc1tbImV4cHJlc3Npb24iXV0sCiAgICAgICAgICAgICB0b3BfYW5ub3RhdGlvbiA9IGhhX3RvcCwKICAgICAgICAgICAgIHJpZ2h0X2Fubm90YXRpb24gPSBoYV9yaWdodCwKICAgICAgICAgICAgIHNob3dfY29sdW1uX25hbWVzID0gRkFMU0UsCiAgICAgICAgICAgICBjb2x1bW5fb3JkZXIgPSBjb2x1bW5fb3JkZXIsCiAgICAgICAgICAgICBjb2x1bW5fZ2FwID0gdW5pdCgyLCAibW0iKSwKICAgICAgICAgICAgIGNsdXN0ZXJfcm93cyA9IEZBTFNFLAogICAgICAgICAgICAgcm93X3RpdGxlID0gTlVMTCwKICAgICAgICAgICAgIHJvd19uYW1lc19ncCA9IGdyaWQ6OmdwYXIoZm9udHNpemUgPSAxMCwgZm9udGZhY2UgPSAicGxhaW4iKSwKICAgICAgICAgICAgIHVzZV9yYXN0ZXIgPSBGQUxTRSwKICAgICAgICAgICAgIHNob3dfaGVhdG1hcF9sZWdlbmQgPSBUUlVFLAogICAgICAgICAgICAgYm9yZGVyID0gVFJVRSkKCkNvbXBsZXhIZWF0bWFwOjpkcmF3KGh0LAogICAgICAgICAgICAgICAgICAgICBtZXJnZV9sZWdlbmQgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICBoZWF0bWFwX2xlZ2VuZF9zaWRlID0gImJvdHRvbSIsCiAgICAgICAgICAgICAgICAgICAgIGFubm90YXRpb25fbGVnZW5kX3NpZGUgPSAiYm90dG9tIikKYGBgCgpWaW9saW4gcGxvdCBvZiBJRklUTTMgOgoKYGBge3IgZmlnX2hmc2NfaWZpdG0zLCBmaWcud2lkdGggPSAzLCBmaWcuaGVpZ2h0ID0gNH0KdGFibGUoc3Vic29iaiRzYW1wbGVfdHlwZSkKCmlmaXRtM19ocyA9IHN1YnNvYmpAYXNzYXlzJFJOQUBkYXRhWyJJRklUTTMiLCBzdWJzb2JqJHNhbXBsZV90eXBlID09ICJIUyJdCmlmaXRtM19oZCA9IHN1YnNvYmpAYXNzYXlzJFJOQUBkYXRhWyJJRklUTTMiLCBzdWJzb2JqJHNhbXBsZV90eXBlID09ICJIRCJdCmlmaXRtM19oc19WU19pZml0bTNfaGQgPSBzdGF0czo6dC50ZXN0KGlmaXRtM19ocywgaWZpdG0zX2hkKQppZml0bTNfaHNfVlNfaWZpdG0zX2hkCgpTZXVyYXQ6OlZsblBsb3Qoc3Vic29iaiwgZ3JvdXAuYnkgPSAic2FtcGxlX3R5cGUiLAogICAgICAgICAgICAgICAgZmVhdHVyZXMgPSAiSUZJVE0zIiwgY29scyA9IHNhbXBsZV90eXBlX2NvbG9ycykgKwogIGdncGxvdDI6OnRoZW1lKGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCmBgYAoKU3BsaXQgYnkgc2FtcGxlIDoKCmBgYHtyIGZpZ19oZnNjX2lmaXRtM19zcGxpdCwgZmlnLndpZHRoID0gNiwgZmlnLmhlaWdodCA9IDR9ClNldXJhdDo6VmxuUGxvdChzdWJzb2JqLCBncm91cC5ieSA9ICJzYW1wbGVfaWRlbnRpZmllciIsCiAgICAgICAgICAgICAgICBmZWF0dXJlcyA9ICJJRklUTTMiLCBjb2xzID0gc2FtcGxlX2luZm8kY29sb3IpICsKICBnZ3Bsb3QyOjp0aGVtZShheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQpgYGAKCgpWaW9saW4gcGxvdCBvZiBERElUNCA6CgpgYGB7ciBmaWdfaGZzY19kZGl0NCwgZmlnLndpZHRoID0gMywgZmlnLmhlaWdodCA9IDR9CnRhYmxlKHN1YnNvYmokc2FtcGxlX3R5cGUpCgpERElUNF9ocyA9IHN1YnNvYmpAYXNzYXlzJFJOQUBkYXRhWyJERElUNCIsIHN1YnNvYmokc2FtcGxlX3R5cGUgPT0gIkhTIl0KRERJVDRfaGQgPSBzdWJzb2JqQGFzc2F5cyRSTkFAZGF0YVsiRERJVDQiLCBzdWJzb2JqJHNhbXBsZV90eXBlID09ICJIRCJdCkRESVQ0X2hzX1ZTX0RESVQ0X2hkID0gc3RhdHM6OnQudGVzdChERElUNF9ocywgRERJVDRfaGQpCkRESVQ0X2hzX1ZTX0RESVQ0X2hkCgpTZXVyYXQ6OlZsblBsb3Qoc3Vic29iaiwgZ3JvdXAuYnkgPSAic2FtcGxlX3R5cGUiLAogICAgICAgICAgICAgICAgZmVhdHVyZXMgPSAiRERJVDQiLCBjb2xzID0gc2FtcGxlX3R5cGVfY29sb3JzKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKYGBgCgpTcGxpdCBieSBzYW1wbGUgOgoKYGBge3IgZmlnX2hmc2NfZGRpdDRfc3BsaXQsIGZpZy53aWR0aCA9IDYsIGZpZy5oZWlnaHQgPSA0fQpTZXVyYXQ6OlZsblBsb3Qoc3Vic29iaiwgZ3JvdXAuYnkgPSAic2FtcGxlX2lkZW50aWZpZXIiLAogICAgICAgICAgICAgICAgZmVhdHVyZXMgPSAiRERJVDQiLCBjb2xzID0gc2FtcGxlX2luZm8kY29sb3IpICsKICBnZ3Bsb3QyOjp0aGVtZShheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQpgYGAKCldlIHJlcHJlc2VudCBzb21lIGdlbmVzIHNwbGl0IGJ5IHNhbXBsZSB0eXBlIDoKCmBgYHtyIGZpZ19oZnNjX2dlbmVfc3BsaXQsIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gNH0KcGxvdF9saXN0ID0gbGFwcGx5KGMoIkRESVQ0IiwgIklGSVRNMyIpLCBGVU4gPSBmdW5jdGlvbihvbmVfZ2VuZSkgewogIHAgPSBhcXVhcml1czo6cGxvdF9zcGxpdF9kaW1yZWQoc29ial9oZnNjLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVkdWN0aW9uID0gbmFtZTJELAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3BsaXRfYnkgPSAic2FtcGxlX3R5cGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3JfYnkgPSBvbmVfZ2VuZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yX3BhbGV0dGUgPSBjKCJncmF5NzAiLCAiI0ZEQkI4NCIsICIjRUY2NTQ4IiwgIiM3RjAwMDAiLCAiYmxhY2siKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1haW5fcHRfc2l6ZSA9IDAuNiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJnX3B0X3NpemUgPSAwLjYsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvcmRlciA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBiZ19jb2xvciA9ICJncmF5OTUiKQogIHAgPSBwYXRjaHdvcms6OndyYXBfcGxvdHMocCwgbnJvdyA9IDEpICsKICAgIHBhdGNod29yazo6cGxvdF9sYXlvdXQoZ3VpZGVzID0gImNvbGxlY3QiKSArCiAgICBnZ3Bsb3QyOjp0aGVtZShsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiKSAmCiAgICBnZ3Bsb3QyOjp0aGVtZShwbG90LnN1YnRpdGxlID0gZWxlbWVudF9ibGFuaygpKQogIHJldHVybihwKQp9KQoKcGF0Y2h3b3JrOjp3cmFwX3Bsb3RzKHBsb3RfbGlzdCwgbmNvbCA9IDIpCmBgYAoKCkJhcnBsb3Qgd2l0aCBudW1iZXIgb2YgSEZTQ3MgYW5kIHRvdGFsIG51bWJlciBvZiBjZWxscyA6CgpgYGB7ciBmaWdfaGZzY19iYXJwbG90LCBmaWcud2lkdGggPSA1LjUsIGZpZy5oZWlnaHQgPSA0LjV9CnF1YW50aWYgPSBkcGx5cjo6bGVmdF9qb2luKAogIHggPSB0YWJsZShzb2JqJHNhbXBsZV9pZGVudGlmaWVyKSAlPiUKICAgIGFzLmRhdGEuZnJhbWUudGFibGUoKSAlPiUKICAgIGBjb2xuYW1lczwtYChjKCJTYW1wbGUiLCAibmJfY2VsbHMiKSksCiAgeSA9IHRhYmxlKHNvYmpfaGZzYyRzYW1wbGVfaWRlbnRpZmllcikgJT4lCiAgICBhcy5kYXRhLmZyYW1lLnRhYmxlKCkgJT4lCiAgICBgY29sbmFtZXM8LWAoYygiU2FtcGxlIiwgIm5iX2hmc2MiKSksCiAgYnkgPSAiU2FtcGxlIikgJT4lCiAgZHBseXI6Om11dGF0ZShwcm9wX2hmc2MgPSByb3VuZCgxMDAqbmJfaGZzYyAvIG5iX2NlbGxzLCAyKSkKCnF1YW50aWZfdG9fcGxvdCA9IHJiaW5kLmRhdGEuZnJhbWUoCiAgZGF0YS5mcmFtZShTYW1wbGUgPSBxdWFudGlmJFNhbXBsZSwKICAgICAgICAgICAgIG5iX2NlbGxzID0gcXVhbnRpZiRuYl9jZWxscyAtIHF1YW50aWYkbmJfaGZzYywKICAgICAgICAgICAgIGNlbGxfdHlwZSA9ICJvdGhlcnMiLAogICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKSwKICBkYXRhLmZyYW1lKFNhbXBsZSA9IHF1YW50aWYkU2FtcGxlLAogICAgICAgICAgICAgbmJfY2VsbHMgPSBxdWFudGlmJG5iX2hmc2MsCiAgICAgICAgICAgICBjZWxsX3R5cGUgPSAiaGZzYyIsCiAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpKSAlPiUKICBkcGx5cjo6bXV0YXRlKGNlbGxfdHlwZSA9IGZhY3RvcihjZWxsX3R5cGUsIGxldmVscyA9IGMoIm90aGVycyIsICJoZnNjIikpKQoKYXF1YXJpdXM6OnBsb3RfYmFycGxvdChkZiA9IHF1YW50aWZfdG9fcGxvdCwKICAgICAgICAgICAgICAgICAgICAgICB4ID0gIlNhbXBsZSIsIHkgPSAibmJfY2VsbHMiLCBmaWxsID0gImNlbGxfdHlwZSIsCiAgICAgICAgICAgICAgICAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9maWxsKCkpICsKICBnZ3Bsb3QyOjpnZW9tX2xhYmVsKGRhdGEgPSBxdWFudGlmLCBpbmhlcml0LmFlcyA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgYWVzKHggPSAuZGF0YSRTYW1wbGUsIHkgPSAwLjA1Ky5kYXRhJHByb3BfaGZzYy8xMDAsIGxhYmVsID0gLmRhdGEkbmJfaGZzYyksCiAgICAgICAgICAgICAgICAgICAgICBsYWJlbC5zaXplID0gMCwgc2l6ZSA9IDUsIGZpbGwgPSBOQSkgKwogIGdncGxvdDI6OnNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoImdyYXk5MCIsIGNvbG9yX21hcmtlcnNbWyJIRlNDIl1dKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBjKCJvdGhlcnMiLCAiaGZzYyIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSAiQ2VsbCBUeXBlIikgKwogIGdncGxvdDI6OnNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgMSwgYnkgPSAwLjI1KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gcGFzdGUwKHNlcSgwLCAxMDAsIGJ5ID0gMjUpLCBzZXAgPSAiICUiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZXhwYW5kID0gZ2dwbG90Mjo6ZXhwYW5zaW9uKGFkZCA9IGMoMCwgMC4wNSkpKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgIGF4aXMubGluZS54ID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJsaWdodGdyYXkiKSwKICAgICAgICAgICAgICAgICB0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNSksCiAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQpgYGAKCiMgSUJMIGFuZCBPUlMKCiMjIFNldHRpbmdzCgpXZSBsb2FkIHRoZSBJQkwgKyBPUlMgZGF0YXNldCA6CgpgYGB7ciBzb2JqX2libG9yc30Kc29ial9pYmxvcnMgPSByZWFkUkRTKHBhc3RlMChkYXRhX2RpciwgIi80X3pvb20vM196b29tX2libG1vcnMvaWJsbW9yc19zb2JqLnJkcyIpKQpzb2JqX2libG9ycwpgYGAKCgpUaGlzIGlzIHRoZSBwcm9qZWN0aW9uIG5hbWUgdG8gdmlzdWFsaXplIGNlbGxzIDoKCmBgYHtyIHNvYmpfbmFtZTJEX2libG9yc30KbmFtZTJEID0gImhhcm1vbnlfMjBfdHNuZSIKYGBgCgpUbyByZXByZXNlbnQgcmVzdWx0cyBmcm9tIGRpZmZlcmVudGlhbCBleHByZXNzaW9uLCB3ZSBsb2FkIHRoZSBhbmFseXNlcyByZXN1bHRzIDoKCmBgYHtyIGxpc3RfcmVzdWx0c19pYmxvcnN9Cmxpc3RfcmVzdWx0cyA9IHJlYWRSRFMocGFzdGUwKGRhdGFfZGlyLCAiLzRfem9vbS8zX3pvb21faWJsbW9ycy9pYmxtb3JzX2xpc3RfcmVzdWx0cy5yZHMiKSkKCmxhcHBseShsaXN0X3Jlc3VsdHMsIEZVTiA9IG5hbWVzKQpgYGAKCiMjIFByZXBhcmF0aW9uCgpXZSBkZWZpbmVkIGNsdXN0ZXIgdHlwZSA6CgpgYGB7ciBjbHVzdGVyX3R5cGVfaWJsb3JzLCBmaWcud2lkdGggPSA3LCBmaWcuaGVpZ2h0ID0gNX0KY2x1c3Rlcl90eXBlID0gdGFibGUoc29ial9pYmxvcnMkY2VsbF90eXBlLCBzb2JqX2libG9ycyRzZXVyYXRfY2x1c3RlcnMpICU+JQogIHByb3AudGFibGUoLiwgbWFyZ2luID0gMikgJT4lCiAgYXBwbHkoLiwgMiwgd2hpY2gubWF4KQpjbHVzdGVyX3R5cGUgPSBzZXROYW1lcyhubSA9IG5hbWVzKGNsdXN0ZXJfdHlwZSksCiAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyhzb2JqX2libG9ycyRjZWxsX3R5cGUpW2NsdXN0ZXJfdHlwZV0pCgpzb2JqX2libG9ycyRjbHVzdGVyX3R5cGUgPSBjbHVzdGVyX3R5cGVbc29ial9pYmxvcnMkc2V1cmF0X2NsdXN0ZXJzXQpzb2JqX2libG9ycyRjbHVzdGVyX3R5cGUgPSBmYWN0b3Ioc29ial9pYmxvcnMkY2x1c3Rlcl90eXBlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gYygiSUJMIiwgIk9SUyIpKQpgYGAKCiMjIEZpZ3VyZXMKCklCTCArIE9SUyBvbiB0aGUgZnVsbCBhdGxhcyA6CgpgYGB7ciBmaWdfaWJsb3JzX2xvY2F0aW9uLCBmaWcud2lkdGggPSAyLCBmaWcuaGVpZ2h0ID0gMn0Kc29iaiRjZWxsX2JjID0gY29sbmFtZXMoc29iaikKc29ial9pYmxvcnMkY2VsbF9iYyA9IGNvbG5hbWVzKHNvYmpfaWJsb3JzKQpzb2JqJGlzX2libG9ycyA9IGRwbHlyOjpsZWZ0X2pvaW4oc29iakBtZXRhLmRhdGFbLCBjKCJjZWxsX2JjIiwgInBlcmNlbnQubXQiKV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzb2JqX2libG9yc0BtZXRhLmRhdGFbLCBjKCJjZWxsX2JjIiwgImNsdXN0ZXJfdHlwZSIpXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJ5ID0gImNlbGxfYmMiKVssICJjbHVzdGVyX3R5cGUiXQoKU2V1cmF0OjpEaW1QbG90KHNvYmosIHJlZHVjdGlvbiA9IG5hbWUyRF9hdGxhcywgcHQuc2l6ZSA9IDAuMDAwMDAxLAogICAgICAgICAgICAgICAgZ3JvdXAuYnkgPSAiaXNfaWJsb3JzIiwgb3JkZXIgPSAiVFJVRSIpICsKICBnZ3Bsb3QyOjpzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYyhjb2xvcl9tYXJrZXJzW2MoIklCTCIsICJPUlMiKV0sIGJnX2NvbG9yKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gYygiSUJMIiwgIk9SUyIsIE5BKSwgbmEudmFsdWUgPSBiZ19jb2xvcikgKwogIGdncGxvdDI6OmxhYnModGl0bGUgPSAiSUJMICsgT1JTIiwKICAgICAgICAgICAgICAgIHN1YnRpdGxlID0gcGFzdGUwKG5jb2woc29ial9pYmxvcnMpLCAiIGNlbGxzIikpICsKICBnZ3Bsb3QyOjp0aGVtZShhc3BlY3QucmF0aW8gPSAxLAogICAgICAgICAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIpLAogICAgICAgICAgICAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKSArCiAgU2V1cmF0OjpOb0F4ZXMoKSArIFNldXJhdDo6Tm9MZWdlbmQoKQpgYGAKCgpQcm9qZWN0IG5hbWUgOgoKYGBge3IgZmlnX2libG9yc19wcm9qZWN0X25hbWUsIGZpZy53aWR0aCA9IDQsIGZpZy5oZWlnaHQgPSA0fQojIFJhbmRvbSBvcmRlcgpzZXQuc2VlZCgxMjM0KQpybmRfb3JkZXIgPSBzYW1wbGUoY29sbmFtZXMoc29ial9pYmxvcnMpLCByZXBsYWNlID0gRkFMU0UsIHNpemUgPSBuY29sKHNvYmpfaWJsb3JzKSkKCiMgRXh0cmFjdCBjb29yZGluYXRlcwpjZWxsc19jb29yZCA9IHNvYmpfaWJsb3JzQHJlZHVjdGlvbnNbW25hbWUyRF1dQGNlbGwuZW1iZWRkaW5ncyAlPiUKICBhcy5kYXRhLmZyYW1lKCkgJT4lCiAgYGNvbG5hbWVzPC1gKGMoIkRpbTEiLCAiRGltMiIpKQpjZWxsc19jb29yZCRwcm9qZWN0X25hbWUgPSBzb2JqX2libG9ycyRwcm9qZWN0X25hbWUKY2VsbHNfY29vcmQgPSBjZWxsc19jb29yZFsocm5kX29yZGVyKSwgXQoKIyBQbG90CmdncGxvdDI6OmdncGxvdChjZWxsc19jb29yZCwgYWVzKHggPSBEaW0xLCB5ID0gRGltMiwgY29sID0gcHJvamVjdF9uYW1lKSkgKwogIGdncGxvdDI6Omdlb21fcG9pbnQoc2l6ZSA9IDEuMikgKwogIGdncGxvdDI6OnNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBzYW1wbGVfaW5mbyRjb2xvciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gc2FtcGxlX2luZm8kcHJvamVjdF9uYW1lKSArCiAgZ2dwbG90Mjo6dGhlbWVfdm9pZCgpICsKICBnZ3Bsb3QyOjp0aGVtZShhc3BlY3QucmF0aW8gPSAxLAogICAgICAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKYGBgCgpDbHVzdGVyIDoKCmBgYHtyIGZpZ19pYmxvcnNfY2x1c3RlcnMsIGZpZy53aWR0aCA9IDQsIGZpZy5oZWlnaHQgPSA0fQpTZXVyYXQ6OkRpbVBsb3Qoc29ial9pYmxvcnMsIHJlZHVjdGlvbiA9IG5hbWUyRCwgcHQuc2l6ZSA9IDEsCiAgICAgICAgICAgICAgICBncm91cC5ieSA9ICJzZXVyYXRfY2x1c3RlcnMiLCBjb2xzID0gZ3JleV9wYWxldHRlLAogICAgICAgICAgICAgICAgbGFiZWwgPSBUUlVFLCBsYWJlbC5zaXplID0gOCkgKwogIGdncGxvdDI6OnRoZW1lKGFzcGVjdC5yYXRpbyA9IDEpICsKICBTZXVyYXQ6Ok5vQXhlcygpICsKICBTZXVyYXQ6Ok5vTGVnZW5kKCkKYGBgCgpDbHVzdGVyIHR5cGUgOgoKYGBge3IgZmlnX2libG9yc19jbHVzdGVyX3R5cGUsIGZpZy53aWR0aCA9IDQsIGZpZy5oZWlnaHQgPSA0fQpTZXVyYXQ6OkRpbVBsb3Qoc29ial9pYmxvcnMsIHJlZHVjdGlvbiA9IG5hbWUyRCwgcHQuc2l6ZSA9IDEsCiAgICAgICAgICAgICAgICBncm91cC5ieSA9ICJjbHVzdGVyX3R5cGUiLCBjb2xzID0gY29sb3JfbWFya2VycykgKwogIGdncGxvdDI6OnRoZW1lKGFzcGVjdC5yYXRpbyA9IDEpICsKICBTZXVyYXQ6Ok5vQXhlcygpICsKICBTZXVyYXQ6Ok5vTGVnZW5kKCkKYGBgCgpDbHVzdGVyIHR5cGUgc3BsaXQgYnkgc2FtcGxlIHR5cGUgOgoKYGBge3IgZmlnX2libG9yc19jbHVzdGVyX3R5cGVfc3BsaXQsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA0fQpwbG90X2xpc3QgPSBhcXVhcml1czo6cGxvdF9zcGxpdF9kaW1yZWQoc29ial9pYmxvcnMsIHJlZHVjdGlvbiA9IG5hbWUyRCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwX2J5ID0gImNsdXN0ZXJfdHlwZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cF9jb2xvciA9IGNvbG9yX21hcmtlcnMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzcGxpdF9ieSA9ICJzYW1wbGVfdHlwZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBiZ19wdF9zaXplID0gMSwgbWFpbl9wdF9zaXplID0gMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJnX2NvbG9yID0gYmdfY29sb3IpCgpwYXRjaHdvcms6OndyYXBfcGxvdHMocGxvdF9saXN0KSAmCiAgU2V1cmF0OjpOb0xlZ2VuZCgpCmBgYAoKCkJhcnBsb3QgYnkgY2x1c3RlciBmYW1pbHkgOgoKYGBge3IgZmlnX2libG9yc19iYXJwbG90LCBmaWcud2lkdGggPSA1LjUsIGZpZy5oZWlnaHQgPSA0LjV9CnNvYmpfaWJsb3JzJGNsdXN0ZXJfdHlwZV9zZXA1ID0gaWZlbHNlKHNvYmpfaWJsb3JzJHNldXJhdF9jbHVzdGVycyA9PSA1LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ZXMgPSAiT1JTXzUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBubyA9IGFzLmNoYXJhY3Rlcihzb2JqX2libG9ycyRjbHVzdGVyX3R5cGUpKSAlPiUKICBhcy5mYWN0b3IoKQoKcXVhbnRpZiA9IHRhYmxlKHNvYmpfaWJsb3JzJHNhbXBsZV9pZGVudGlmaWVyKSAlPiUKICBhcy5kYXRhLmZyYW1lLnRhYmxlKCkgJT4lCiAgYGNvbG5hbWVzPC1gKGMoIlNhbXBsZSIsICJuYl9jZWxscyIpKQoKcXVhbnRpZl90b19wbG90ID0gdGFibGUoc29ial9pYmxvcnMkc2FtcGxlX2lkZW50aWZpZXIsCiAgICAgICAgICAgICAgICAgICAgICAgIHNvYmpfaWJsb3JzJGNsdXN0ZXJfdHlwZV9zZXA1KSAlPiUKICBhcy5kYXRhLmZyYW1lLnRhYmxlKCkgJT4lCiAgYGNvbG5hbWVzPC1gKGMoIlNhbXBsZSIsICJDZWxsVHlwZSIsICJOdW1iZXIiKSkgJT4lCiAgZHBseXI6Om11dGF0ZShTdHlsZSA9IGlmZWxzZShDZWxsVHlwZSA9PSAiT1JTXzUiLCB5ZXMgPSAiSUwxUjIrIiwgbm8gPSAiSUwxUjItIikpICU+JQogIGRwbHlyOjptdXRhdGUoU3R5bGUgPSBmYWN0b3IoU3R5bGUsIGxldmVscyA9IGMoIklMMVIyLSIsICJJTDFSMisiKSkpICU+JQogIGRwbHlyOjptdXRhdGUoQ2VsbFR5cGUgPSBpZmVsc2UoQ2VsbFR5cGUgPT0gIk9SU181IiwgeWVzID0gIk9SUyIsIG5vID0gYXMuY2hhcmFjdGVyKENlbGxUeXBlKSkpICU+JQogIGBjb2xuYW1lczwtYChjKCJTYW1wbGUiLCAiQ2VsbCBUeXBlIiwgIk51bWJlciIsICJJTDFSMiBzdGF0dXMiKSkKCmFxdWFyaXVzOjpwbG90X2JhcnBsb3QoZGYgPSBxdWFudGlmX3RvX3Bsb3QsCiAgICAgICAgICAgICAgICAgICAgICAgeCA9ICJTYW1wbGUiLCB5ID0gIk51bWJlciIsCiAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9ICJDZWxsIFR5cGUiLCBwYXR0ZXJuID0gIklMMVIyIHN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9maWxsKCkpICsKICBnZ3Bsb3QyOjpnZW9tX2xhYmVsKGRhdGEgPSBxdWFudGlmLCBpbmhlcml0LmFlcyA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgYWVzKHggPSAuZGF0YSRTYW1wbGUsIHkgPSAxLjA1LCBsYWJlbCA9IC5kYXRhJG5iX2NlbGxzKSwKICAgICAgICAgICAgICAgICAgICAgIGxhYmVsLnNpemUgPSAwLCBzaXplID0gNSkgKwogIGdncGxvdDI6OnNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHVubGlzdChjb2xvcl9tYXJrZXJzW2xldmVscyhzb2JqX2libG9ycyRjbHVzdGVyX3R5cGUpXSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gbmFtZXMoY29sb3JfbWFya2Vyc1tsZXZlbHMoc29ial9pYmxvcnMkY2x1c3Rlcl90eXBlKV0pLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSAiQ2VsbCBUeXBlIikgKwogIGdncGxvdDI6OnNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgMSwgYnkgPSAwLjI1KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gcGFzdGUwKHNlcSgwLCAxMDAsIGJ5ID0gMjUpLCBzZXAgPSAiICUiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZXhwYW5kID0gZ2dwbG90Mjo6ZXhwYW5zaW9uKGFkZCA9IGMoMCwgMC4wNSkpKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgIGF4aXMubGluZS54ID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJsaWdodGdyYXkiKSwKICAgICAgICAgICAgICAgICB0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNSksCiAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikKYGBgCgoKREUgZ2VuZXMgYmV0d2VlbiBJQkwgYW5kIE9SUyA6CgpgYGB7ciBmaWdfaWJsb3JzX2RlX3BvcCwgZmlnLndpZHRoID0gNiwgZmlnLmhlaWdodCA9IDZ9Cm1hcmsgPSBsaXN0X3Jlc3VsdHMkSUJMX3ZzX09SUyRtYXJrCm1hcmskZ2VuZV9uYW1lID0gcm93bmFtZXMobWFyaykKbWFya19sYWJlbCA9IHJiaW5kKAogICMgdXAtcmVndWxhdGVkIGluIElCTAogIG1hcmsgJT4lIGRwbHlyOjp0b3BfbiguLCBuID0gMjAsIHd0ID0gYXZnX2xvZ0ZDKSwKICAjIHVwLXJlZ3VsYXRlZCBpbiBPUlMKICBtYXJrICU+JSBkcGx5cjo6dG9wX24oLiwgbiA9IDIwLCB3dCA9IC1hdmdfbG9nRkMpLAogICMgcmVwcmVzZW50YXRpdmUgYW5kIHNlbGVjdGl2ZSBmb3IgSUJMCiAgbWFyayAlPiUgZHBseXI6OnRvcF9uKC4sIG4gPSAyMCwgd3QgPSAocGN0LjEgLSBwY3QuMikpLAogICMgcmVwcmVzZW50YXRpdmUgYW5kIHNlbGVjdGl2ZSBmb3IgT1JTCiAgbWFyayAlPiUgZHBseXI6OnRvcF9uKC4sIG4gPSAyMCwgd3QgPSAtKHBjdC4xIC0gcGN0LjIpKSkgJT4lCiAgZHBseXI6OmRpc3RpbmN0KCkKbWFya19sYWJlbCA9IG1hcmtfbGFiZWxbIWdyZXBsKHJvd25hbWVzKG1hcmtfbGFiZWwpLCBwYXR0ZXJuID0gIl5NVCIpLCBdCgphdmdfbG9nRkNfcmFuZ2UgPSBzZXROYW1lcyhjKG1pbihtYXJrX2xhYmVsJGF2Z19sb2dGQyksIC0xLCAwLCAxLCBtYXgobWFya19sYWJlbCRhdmdfbG9nRkMpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgbm0gPSBjKCJkb2RnZXJibHVlNCIsICJkb2RnZXJibHVlMyIsICIjQjdCN0I3IiwgImZpcmVicmljazMiLCAiZmlyZWJyaWNrNCIpKQoKCmdncGxvdDI6OmdncGxvdChtYXJrLCBhZXMoeCA9IHBjdC4xLCB5ID0gcGN0LjIsIGNvbCA9IGF2Z19sb2dGQykpICsKICBnZ3Bsb3QyOjpnZW9tX2FibGluZShzbG9wZSA9IDEsIGludGVyY2VwdCA9IDAsIGx0eSA9IDIpICsKICBnZ3Bsb3QyOjpnZW9tX3BvaW50KCkgKwogIGdncmVwZWw6Omdlb21fbGFiZWxfcmVwZWwoZGF0YSA9IG1hcmtfbGFiZWwsIG1heC5vdmVybGFwcyA9IEluZiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFlcyh4ID0gcGN0LjEsIHkgPSBwY3QuMiwgbGFiZWwgPSBnZW5lX25hbWUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sID0gImJsYWNrIiwgZmlsbCA9IE5BLCBzaXplID0gMy41LCBsYWJlbC5zaXplID0gTkEpICsKICBnZ3Bsb3QyOjpsYWJzKHggPSAiRW5yaWNoZWQgaW4gSUJMIiwKICAgICAgICAgICAgICAgIHkgPSAiRW5yaWNoZWQgaW4gT1JTIikgKwogIGdncGxvdDI6OnNjYWxlX2NvbG9yX2dyYWRpZW50bihjb2xvcnMgPSBuYW1lcyhhdmdfbG9nRkNfcmFuZ2UpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZXMgPSBzY2FsZXM6OnJlc2NhbGUodW5uYW1lKGF2Z19sb2dGQ19yYW5nZSkpKSArCiAgZ2dwbG90Mjo6dGhlbWVfY2xhc3NpYygpICsKICBnZ3Bsb3QyOjp0aGVtZShhc3BlY3QucmF0aW8gPSAxKQpgYGAKCkdTRUEgcGxvdCA6CgpgYGB7ciBmaWdfaWJsb3JzX2tlcmF0aW5pemF0aW9uLCBmaWcud2lkdGggPSA2LCBmaWcuaGVpZ2h0ID0gNH0KdGhlX2dzX25hbWUgPSAiUkVBQ1RPTUVfS0VSQVRJTklaQVRJT04iIAp0aGVfY29udGVudCA9IGxpc3RfcmVzdWx0cyRJQkxfdnNfT1JTJGdzZWFAcmVzdWx0ICU+JQogIGRwbHlyOjpmaWx0ZXIoSUQgPT0gdGhlX2dzX25hbWUpCnRoZV9zdWJ0aXRsZSA9IHBhc3RlMCgiXG5ORVMgOiAiLCByb3VuZCh0aGVfY29udGVudCRORVMsIDIpLAogICAgICAgICAgICAgICAgICAgICAgIiB8IHB2YWx1ZSA6ICIsIHJvdW5kKHRoZV9jb250ZW50JHB2YWx1ZSwgNCksCiAgICAgICAgICAgICAgICAgICAgICAiIHwgc2V0IHNpemUgOiAiLCB0aGVfY29udGVudCRzZXRTaXplLCAiIGdlbmVzIikKCmVucmljaHBsb3Q6OmdzZWFwbG90Mih4ID0gbGlzdF9yZXN1bHRzJElCTF92c19PUlMkZ3NlYSwKICAgICAgICAgICAgICAgICAgICAgIGdlbmVTZXRJRCA9IHRoZV9nc19uYW1lKSArCiAgZ2dwbG90Mjo6bGFicyh0aXRsZSA9IHRoZV9nc19uYW1lLAogICAgICAgICAgICAgICAgc3VidGl0bGUgPSB0aGVfc3VidGl0bGUpICsKICBnZ3Bsb3QyOjp0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBmYWNlID0gImJvbGQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWFyZ2luID0gZ2dwbG90Mjo6bWFyZ2luKDMsIDMsIDUsIDMpKSwKICAgICAgICAgICAgICAgICBwbG90LnN1YnRpdGxlID0gZ2d0ZXh0OjplbGVtZW50X21hcmtkb3duKGhqdXN0ID0gMC41LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IDEwKSkKYGBgCgpgYGB7ciBmaWdfaWJsb3JzX2lmbmEsIGZpZy53aWR0aCA9IDYsIGZpZy5oZWlnaHQgPSA0fQp0aGVfZ3NfbmFtZSA9ICJIQUxMTUFSS19JTlRFUkZFUk9OX0dBTU1BX1JFU1BPTlNFIiAKdGhlX2NvbnRlbnQgPSBsaXN0X3Jlc3VsdHMkSUJMX3ZzX09SUyRnc2VhQHJlc3VsdCAlPiUKICBkcGx5cjo6ZmlsdGVyKElEID09IHRoZV9nc19uYW1lKQp0aGVfc3VidGl0bGUgPSBwYXN0ZTAoIlxuTkVTIDogIiwgcm91bmQodGhlX2NvbnRlbnQkTkVTLCAyKSwKICAgICAgICAgICAgICAgICAgICAgICIgfCBwdmFsdWUgOiAiLCByb3VuZCh0aGVfY29udGVudCRwdmFsdWUsIDQpLAogICAgICAgICAgICAgICAgICAgICAgIiB8IHNldCBzaXplIDogIiwgdGhlX2NvbnRlbnQkc2V0U2l6ZSwgIiBnZW5lcyIpCgplbnJpY2hwbG90Ojpnc2VhcGxvdDIoeCA9IGxpc3RfcmVzdWx0cyRJQkxfdnNfT1JTJGdzZWEsCiAgICAgICAgICAgICAgICAgICAgICBnZW5lU2V0SUQgPSB0aGVfZ3NfbmFtZSkgKwogIGdncGxvdDI6OmxhYnModGl0bGUgPSB0aGVfZ3NfbmFtZSwKICAgICAgICAgICAgICAgIHN1YnRpdGxlID0gdGhlX3N1YnRpdGxlKSArCiAgZ2dwbG90Mjo6dGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgZmFjZSA9ICJib2xkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1hcmdpbiA9IGdncGxvdDI6Om1hcmdpbigzLCAzLCA1LCAzKSksCiAgICAgICAgICAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGdndGV4dDo6ZWxlbWVudF9tYXJrZG93bihoanVzdCA9IDAuNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSAxMCkpCmBgYAoKU2NvcmUgZm9yIGJvdGggZ2VuZSBzZXRzLCBpbiBhbGwgY2VsbHMgOgoKYGBge3IgZmlnX2libG9yc19rZXJhX3Njb3JlLCBmaWcud2lkdGggPSA2LCBmaWcuaGVpZ2h0ID0gNH0KZ2VuZV9zZXRzID0gYXF1YXJpdXM6OmdldF9nZW5lX3NldHMoc3BlY2llcyA9ICJIb21vIHNhcGllbnMiKQp0aGVfZ3NfbmFtZSA9ICJSRUFDVE9NRV9LRVJBVElOSVpBVElPTiIgCnRoZV9nc19jb250ZW50ID0gZ2VuZV9zZXRzJGdlbmVfc2V0c19mdWxsICU+JQogIGRwbHlyOjpmaWx0ZXIoZ3NfbmFtZSA9PSB0aGVfZ3NfbmFtZSkgJT4lCiAgZHBseXI6OnB1bGwoZ2VuZV9zeW1ib2wpICU+JQogIHVubGlzdCgpCgpzb2JqX2libG9ycyRzY29yZV9rZXJhID0gU2V1cmF0OjpBZGRNb2R1bGVTY29yZShzb2JqX2libG9ycywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmVhdHVyZXMgPSBsaXN0KHRoZV9nc19jb250ZW50KSkkQ2x1c3RlcjEKClNldXJhdDo6VmxuUGxvdChzb2JqX2libG9ycywgZmVhdHVyZXMgPSAic2NvcmVfa2VyYSIsIHB0LnNpemUgPSAwLjA1LAogICAgICAgICAgICAgICAgc3BsaXQuYnkgPSAic2FtcGxlX3R5cGUiLCBncm91cC5ieSA9ICJjbHVzdGVyX3R5cGUiLAogICAgICAgICAgICAgICAgY29scyA9IHJldihzYW1wbGVfdHlwZV9jb2xvcnMpKSArCiAgZ2dwbG90Mjo6bGFicyh0aXRsZSA9IHRoZV9nc19uYW1lKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpKQpgYGAKCmBgYHtyIGZpZ19pYmxvcnNfaWZuYV9zY29yZSwgZmlnLndpZHRoID0gNiwgZmlnLmhlaWdodCA9IDR9CnRoZV9nc19uYW1lID0gIkhBTExNQVJLX0lOVEVSRkVST05fR0FNTUFfUkVTUE9OU0UiIAp0aGVfZ3NfY29udGVudCA9IGdlbmVfc2V0cyRnZW5lX3NldHNfZnVsbCAlPiUKICBkcGx5cjo6ZmlsdGVyKGdzX25hbWUgPT0gdGhlX2dzX25hbWUpICU+JQogIGRwbHlyOjpwdWxsKGdlbmVfc3ltYm9sKSAlPiUKICB1bmxpc3QoKQoKc29ial9pYmxvcnMkc2NvcmVfaWZuYSA9IFNldXJhdDo6QWRkTW9kdWxlU2NvcmUoc29ial9pYmxvcnMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZlYXR1cmVzID0gbGlzdCh0aGVfZ3NfY29udGVudCkpJENsdXN0ZXIxCgpTZXVyYXQ6OlZsblBsb3Qoc29ial9pYmxvcnMsIGZlYXR1cmVzID0gInNjb3JlX2lmbmEiLCBwdC5zaXplID0gMC4wNSwKICAgICAgICAgICAgICAgIHNwbGl0LmJ5ID0gInNhbXBsZV90eXBlIiwgZ3JvdXAuYnkgPSAiY2x1c3Rlcl90eXBlIiwKICAgICAgICAgICAgICAgIGNvbHMgPSByZXYoc2FtcGxlX3R5cGVfY29sb3JzKSkgKwogIGdncGxvdDI6OmxhYnModGl0bGUgPSB0aGVfZ3NfbmFtZSkgKwogIGdncGxvdDI6OnRoZW1lKGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSkKYGBgCgoKVmlvbGluIHBsb3QgZm9yIElCTCA6CgpgYGB7ciBmaWdfaWJsb3JzX2libCwgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSAyLjV9CnN1YnNvYmogPSBzdWJzZXQoc29ial9pYmxvcnMsIGNsdXN0ZXJfdHlwZSA9PSAiSUJMIikKClNldXJhdDo6VmxuUGxvdChzdWJzb2JqLCBncm91cC5ieSA9ICJzYW1wbGVfdHlwZSIsIHB0LnNpemUgPSAwLjA1LAogICAgICAgICAgICAgICAgZmVhdHVyZXMgPSBjKCJEVVNQMSIsICJERElUNCIsICJNSUYiLCAiTEdBTFM3IiwgIkFSRjUiLCAiUzEwMEE5IiksCiAgICAgICAgICAgICAgICBjb2xzID0gc2FtcGxlX3R5cGVfY29sb3JzLCBuY29sID0gNikgJgogIGdncGxvdDI6OnRoZW1lKGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQpgYGAKClZpb2xpbiBwbG90IGZvciBPUlMgOgoKYGBge3IgZmlnX2libG9yc19vcnMsIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gMi41fQpzdWJzb2JqID0gc3Vic2V0KHNvYmpfaWJsb3JzLCBjbHVzdGVyX3R5cGUgPT0gIk9SUyIpCgpTZXVyYXQ6OlZsblBsb3Qoc3Vic29iaiwgZ3JvdXAuYnkgPSAic2FtcGxlX3R5cGUiLCBwdC5zaXplID0gMC4wNSwKICAgICAgICAgICAgICAgIGZlYXR1cmVzID0gYygiRFVTUDEiLCAiS0xGNiIsICJDTEROMSIsICJDVEdGIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUzEwMEE5IiwgIkNDTDIiLCAiSUZJVE0zIiwgIklGSTI3IiksCiAgICAgICAgICAgICAgICBjb2xzID0gc2FtcGxlX3R5cGVfY29sb3JzLCBuY29sID0gOCkgJgogIGdncGxvdDI6OnRoZW1lKGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQpgYGAKClNwbGl0IGJ5IHNhbXBsZSA6CgpgYGB7ciBmaWdfaWJsb3JzX29yc19zcGxpdCwgZmlnLndpZHRoID0gNiwgZmlnLmhlaWdodCA9IDR9ClNldXJhdDo6VmxuUGxvdChzdWJzb2JqLCBncm91cC5ieSA9ICJzYW1wbGVfaWRlbnRpZmllciIsCiAgICAgICAgICAgICAgICBmZWF0dXJlcyA9ICJJRkkyNyIsIGNvbHMgPSBzYW1wbGVfaW5mbyRjb2xvcikgKwogIGdncGxvdDI6OnRoZW1lKGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCmBgYAoKSGVhdG1hcCBmb3IgY2x1c3RlciA1IHZzIG90aGVyIE9SUyA6CgpgYGB7ciBmaWdfaWJsb3JzX2hlYXRtYXBfY2x1c3RlcjUsIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gMTksIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUifQpzdWJzb2JqID0gc3Vic2V0KHNvYmpfaWJsb3JzLCBjbHVzdGVyX3R5cGUgPT0gIk9SUyIpCgpmZWF0dXJlc19vaSA9IGMoIllCWDMiLCAiVFhOSVAiLCAiS1JUMTQiLCAiS1JUMTUiLCAiTkVBVDEiLAogICAgICAgICAgICAgICAgIkZYWUQzIiwgIk1UMkEiLCAiTVQxRSIsICJNVDFYIiwgIkFRUDMiLCAiR0xVTCIsCiAgICAgICAgICAgICAgICAjICJIQUxMTUFSS19UTkZBX1NJR05BTElOR19WSUFfTkZLQiIKICAgICAgICAgICAgICAgICJGT1MiLCAiSlVOQiIsICJEVVNQMSIsICJaRlAzNiIsICJORktCSVoiLAogICAgICAgICAgICAgICAgIkFURjMiLCAiUkhPQiIsICAiRVRTMiIsICJJTDE4IiwgIktMRjQiLCAiS0xGNiIsICJLTEY5IiwKICAgICAgICAgICAgICAgICJLTEYzIiwgIktMRjUiLCAiQ09MMTdBMSIsICJUSFNENCIsICJXTlQzIiwgIldOVDQiLCAiU0xQSSIsICJQTEFUIiwKICAgICAgICAgICAgICAgICJMQU1CNCIsICJEQ04iLCAiU1BJTks1IiwKICAgICAgICAgICAgICAgICJHU1RNMyIsICJBTERIM0ExIiwgICJMR0FMUzdCIiwgIlNMQzM4QTIiLCAiRUhGIiwgICJDTEVDMkIiLAogICAgICAgICAgICAgICAgIklMMjBSQiIsICJJTDFSMiIsICJJRkkyNyIsICJDWENMMTQiLCAiSExBLUMiLCAiR1BTTTIiLCAiREFBTTEiLCAgICJJRDEiLAogICAgICAgICAgICAgICAgIlJOQVNFVDIiLCAiSE9QWCIsICJQT1UzRjEiLCAiU1BSWTEiLCAiQVIiLCAiUERHRkMiLAogICAgICAgICAgICAgICAgIldGREMyIiwgIldGREM1IiwgIlRTQzIyRDMiLCAiRkdGUjMiLCAgIkxZNkQiLCAiSUdGQlAzIiwgCiAgICAgICAgICAgICAgICAjIE90aGVyIE9SUwogICAgICAgICAgICAgICAgIkFQT0UiLCAiQ1RTQiIsICJDQUxEMSIsICJTT1g0IiwKICAgICAgICAgICAgICAgICJTVE1OMSIsICJMTU80IiwgIkNFQlBCIiwgIlRNRU00NUEiLCAiR1BYMiIsICJDMVFUTkYxMiIsICJHSkI2IiwKICAgICAgICAgICAgICAgICJLUlQ2QSIsICJLUlQxNyIsICJSQlAxIiwgIkNBTE1MMyIsICJQVE4iLCAiREFQSzIiLAogICAgICAgICAgICAgICAgIkVHTE4zIiwgIkZJTElQMUwiLCAiQURHUkwzIiwgIkZTVCIsICJFRk5CMiIsICJTRU1BNUEiLAogICAgICAgICAgICAgICAgIkZHRlIxIiwgIkVHUjIiLCAiQ0xETjEiLCAiREVGQjEiLCAiQ0FSRDE4IiwgIk1HU1QxIikKCiMgTWF0cml4Cm1hdF9leHByID0gU2V1cmF0OjpHZXRBc3NheURhdGEoc3Vic29iaikKbWF0X2V4cHIgPSBtYXRfZXhwcltmZWF0dXJlc19vaSwgXQptYXRfZXhwciA9IE1hdHJpeDo6dChtYXRfZXhwcikKbWF0X2V4cHIgPSBkeW51dGlsczo6c2NhbGVfcXVhbnRpbGUobWF0X2V4cHIpICMgYmV0d2VlbiAwIGFuZCAxCm1hdF9leHByID0gTWF0cml4Ojp0KG1hdF9leHByKQpkaW0obWF0X2V4cHIpICMgZ2VuZXMgeCBjZWxscwoKIyMgQ29sb3JzCmxpc3RfY29sb3JzID0gbGlzdCgpCmxpc3RfY29sb3JzW1siZXhwcmVzc2lvbiJdXSA9IHJldihSQ29sb3JCcmV3ZXI6OmJyZXdlci5wYWwobmFtZSA9ICJSZEJ1IiwgbiA9IDkpKQpsaXN0X2NvbG9yc1tbInNhbXBsZV90eXBlIl1dID0gc2FtcGxlX3R5cGVfY29sb3JzCmxpc3RfY29sb3JzW1sic2FtcGxlX2lkZW50aWZpZXIiXV0gPSBzZXROYW1lcyhubSA9IHNhbXBsZV9pbmZvJHNhbXBsZV9pZGVudGlmaWVyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2FtcGxlX2luZm8kY29sb3IpCmxpc3RfY29sb3JzW1sicG9wdWxhdGlvbiJdXSA9IHNldE5hbWVzKG5tID0gYygiSUwxUjIrIE9SUyIsICJvdGhlciBPUlMiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYygiYmxhY2siLCBjb2xvcl9tYXJrZXJzWyJPUlMiXSkpCmxpc3RfY29sb3JzW1sibkZlYXR1cmVfUk5BIl1dID0gY2lyY2xpemU6OmNvbG9yUmFtcDIoYnJlYWtzID0gc2VxKGZyb20gPSBtaW4oc3Vic29iaiRuRmVhdHVyZV9STkEpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0byA9IG1heChzdWJzb2JqJG5GZWF0dXJlX1JOQSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxlbmd0aC5vdXQgPSA5KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvcnMgPSBSQ29sb3JCcmV3ZXI6OmJyZXdlci5wYWwobmFtZSA9ICJHcmV5cyIsIG4gPSA5KSkKCiMgQ2VsbHMgb3JkZXIKY29sdW1uX29yZGVyID0gc3Vic29iakBtZXRhLmRhdGEgJT4lCiAgZHBseXI6Om11dGF0ZShzZXVyYXRfY2x1c3RlcnMgPSBmYWN0b3Ioc2V1cmF0X2NsdXN0ZXJzLCBsZXZlbHMgPSBjKDUsIDMsIDAsIDEsIDcpKSkgJT4lCiAgZHBseXI6OmFycmFuZ2Uoc2FtcGxlX3R5cGUsIHNldXJhdF9jbHVzdGVycywgc2FtcGxlX2lkZW50aWZpZXIpICU+JQogIHJvd25hbWVzKCkKY29sdW1uX29yZGVyID0gbWF0Y2goY29sdW1uX29yZGVyLCByb3duYW1lcyhzdWJzb2JqQG1ldGEuZGF0YSkpCgojIEFubm90YXRpb24KaGFfdG9wID0gSGVhdG1hcEFubm90YXRpb24oc2FtcGxlX3R5cGUgPSBzdWJzb2JqJHNhbXBsZV90eXBlLAogICAgICAgICAgICAgICAgICAgICAgICAgICBzYW1wbGVfaWRlbnRpZmllciA9IHN1YnNvYmokc2FtcGxlX2lkZW50aWZpZXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHBvcHVsYXRpb24gPSBpZmVsc2Uoc3Vic29iaiRjbHVzdGVyX3R5cGVfc2VwNSA9PSAiT1JTIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ZXMgPSAib3RoZXIgT1JTIiwgbm8gPSAiSUwxUjIrIE9SUyIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICBjb2wgPSBsaXN0KHNhbXBsZV90eXBlID0gbGlzdF9jb2xvcnNbWyJzYW1wbGVfdHlwZSJdXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzYW1wbGVfaWRlbnRpZmllciA9IGxpc3RfY29sb3JzW1sic2FtcGxlX2lkZW50aWZpZXIiXV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcG9wdWxhdGlvbiA9IGxpc3RfY29sb3JzW1sicG9wdWxhdGlvbiJdXSkpCgpoYV9ib3R0b20gPSBIZWF0bWFwQW5ub3RhdGlvbihuRmVhdHVyZV9STkEgPSBzdWJzb2JqJG5GZWF0dXJlX1JOQSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sID0gbGlzdChuRmVhdHVyZV9STkEgPSBsaXN0X2NvbG9yc1tbIm5GZWF0dXJlX1JOQSJdXSkpCgojIEhlYXRtYXAKaHQgPSBIZWF0bWFwKGFzLm1hdHJpeChtYXRfZXhwciksCiAgICAgICAgICAgICBoZWF0bWFwX2xlZ2VuZF9wYXJhbSA9IGxpc3QodGl0bGUgPSAiRXhwcmVzc2lvbiIsIGF0ID0gYygwLCAxKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygibG93IiwgImhpZ2giKSksCiAgICAgICAgICAgICBjb2wgPSBsaXN0X2NvbG9yc1tbImV4cHJlc3Npb24iXV0sCiAgICAgICAgICAgICB0b3BfYW5ub3RhdGlvbiA9IGhhX3RvcCwKICAgICAgICAgICAgIGJvdHRvbV9hbm5vdGF0aW9uID0gaGFfYm90dG9tLAogICAgICAgICAgICAgIyBDZWxsIGdyb3VwaW5nCiAgICAgICAgICAgICBjb2x1bW5fc3BsaXQgPSBzdWJzb2JqJHNhbXBsZV90eXBlICU+JSBhcy5jaGFyYWN0ZXIoKSwKICAgICAgICAgICAgIGNsdXN0ZXJfY29sdW1ucyA9IEZBTFNFLAogICAgICAgICAgICAgY29sdW1uX29yZGVyID0gY29sdW1uX29yZGVyLAogICAgICAgICAgICAgY29sdW1uX3RpdGxlID0gTlVMTCwKICAgICAgICAgICAgIHNob3dfY29sdW1uX2RlbmQgPSBGQUxTRSwKICAgICAgICAgICAgIHNob3dfY29sdW1uX25hbWVzID0gRkFMU0UsCiAgICAgICAgICAgICAjIEdlbmVzCiAgICAgICAgICAgICBjbHVzdGVyX3Jvd3MgPSBGQUxTRSwKICAgICAgICAgICAgIHJvd19uYW1lc19ncCA9IGdyaWQ6OmdwYXIoZm9udHNpemUgPSAxNCwgZm9udGZhY2UgPSAicGxhaW4iKSwKICAgICAgICAgICAgICMgU3R5bGUKICAgICAgICAgICAgIHVzZV9yYXN0ZXIgPSBGQUxTRSwKICAgICAgICAgICAgIHNob3dfaGVhdG1hcF9sZWdlbmQgPSBUUlVFLAogICAgICAgICAgICAgYm9yZGVyID0gVFJVRSkKCkNvbXBsZXhIZWF0bWFwOjpkcmF3KGh0LAogICAgICAgICAgICAgICAgICAgICBtZXJnZV9sZWdlbmQgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICBoZWF0bWFwX2xlZ2VuZF9zaWRlID0gInJpZ2h0IiwKICAgICAgICAgICAgICAgICAgICAgYW5ub3RhdGlvbl9sZWdlbmRfc2lkZSA9ICJyaWdodCIpCmBgYAoKR2VuZXMgb2YgaW50ZXJlc3QgOgoKYGBge3IgZmlnX2libG9yc19nZW5lcywgZmlnLndpZHRoID0gMiwgZmlnLmhlaWdodCA9IDJ9CmdlbmVzID0gYygiS1JUMTYiLCAiQ09MMTdBMSIsICJEU1QiLCAiS1JUNkIiLCAiSUwxUjIiLCAiV05UMyIsCiAgICAgICAgICAiSUZJMjciLCAiQ1hDTDE0IiwgIklHRkJQMyIsICJLUlQxNSIsICJDRDIwMCIpCgpwbG90X2xpc3QgPSBsYXBwbHkoYygxOmxlbmd0aChnZW5lcykpLCBGVU4gPSBmdW5jdGlvbihnZW5lX2lkKSB7CiAgZ2VuZSA9IGdlbmVzW1tnZW5lX2lkXV0KICAKICBzb2JqX2libG9ycyRteV9nZW5lID0gU2V1cmF0OjpGZXRjaERhdGEoc29ial9pYmxvcnMsIGdlbmUpWywgMV0gJT4lCiAgICBhcXVhcml1czo6cnVuX3Jlc2NhbGUoLiwgbmV3X21pbiA9IDAsIG5ld19tYXggPSAxMCkKICAKICBTZXVyYXQ6OkZlYXR1cmVQbG90KHNvYmpfaWJsb3JzLCBmZWF0dXJlcyA9ICJteV9nZW5lIiwgcmVkdWN0aW9uID0gbmFtZTJELCBwdC5zaXplID0gMC4yNSkgKwogICAgZ2dwbG90Mjo6c2NhbGVfY29sb3JfZ3JhZGllbnRuKGNvbG9ycyA9IGFxdWFyaXVzOjpjb2xvcl9nZW5lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IHNlcSgwLCAxMCwgYnkgPSAyLjUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIm1pbiIsIHJlcCgiIiwgMyksICJtYXgiKSkgKwogICAgZ2dwbG90Mjo6bGFicyh0aXRsZSA9IGdlbmUpICsKICAgIGdncGxvdDI6OnRoZW1lKGFzcGVjdC5yYXRpbyA9IDEsCiAgICAgICAgICAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBzaXplID0gMTcpLAogICAgICAgICAgICAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgc2l6ZSA9IDE1KSwKICAgICAgICAgICAgICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNSksCiAgICAgICAgICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsKICAgIFNldXJhdDo6Tm9BeGVzKCkKfSkKCnBsb3RfbGlzdApgYGAKCgojIEhGU0NzIHRvIElCTCBhbmQgT1JTCgojIyBTZXR0aW5ncwoKV2UgbG9hZCB0aGUgbWVyZ2VkIGRhdGFzZXQgOgoKYGBge3Igc29ial90cmFqfQpzb2JqX3RyYWogPSByZWFkUkRTKHBhc3RlMChkYXRhX2RpciwgIi80X3pvb20vNF96b29tX2hmc2NfaWJsbW9ycy9oZnNjX2libG1vcnNfc29ial90cmFqX3RpbmdhLnJkcyIpKQpzb2JqX3RyYWoKYGBgCgoKVGhpcyBpcyB0aGUgcHJvamVjdGlvbiBuYW1lIHRvIHZpc3VhbGl6ZSBjZWxscyA6CgpgYGB7ciBzb2JqX25hbWUyRF90cmFqfQpuYW1lMkQgPSAiaGFybW9ueV9kbSIKYGBgCgpXZSBsb2FkIHRoZSB0cmFqZWN0b3J5IG9iamVjdCBmb3IgdmlzdWFsaXNhdGlvbiBwdXJwb3NlIDoKCmBgYHtyIGxvYWRfdHJhan0KbXlfdHJhaiA9IHJlYWRSRFMocGFzdGUwKGRhdGFfZGlyLCAiLzRfem9vbS80X3pvb21faGZzY19pYmxtb3JzL2hmc2NfaWJsbW9yc19teV90cmFqX3RpbmdhLnJkcyIpKQpjbGFzcyhteV90cmFqKQpgYGAKCgojIyBQcmVwYXJhdGlvbgoKV2UgZGVmaW5lZCBjZWxsIHR5cGUgYmFzZWQgb24gaW5kaXZpZHVhbCBvYmplY3QgOgoKYGBge3IgY2x1c3Rlcl90eXBlX3RyYWosIGZpZy53aWR0aCA9IDcsIGZpZy5oZWlnaHQgPSA1fQpzb2JqX2libG9ycyRjZWxsX2JjID0gY29sbmFtZXMoc29ial9pYmxvcnMpCnNvYmpfdHJhaiRjZWxsX2JjID0gY29sbmFtZXMoc29ial90cmFqKQpzb2JqX3RyYWokY2x1c3Rlcl90eXBlID0gZHBseXI6OmxlZnRfam9pbihzb2JqX3RyYWpAbWV0YS5kYXRhWywgYygiY2VsbF9iYyIsICJwZXJjZW50Lm10IildLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzb2JqX2libG9yc0BtZXRhLmRhdGFbLCBjKCJjZWxsX2JjIiwgImNsdXN0ZXJfdHlwZSIpXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnkgPSAiY2VsbF9iYyIpWywgImNsdXN0ZXJfdHlwZSJdICU+JSBhcy5jaGFyYWN0ZXIoKQpzb2JqX3RyYWokY2x1c3Rlcl90eXBlID0gaWZlbHNlKGNvbG5hbWVzKHNvYmpfdHJhaikgJWluJSBjb2xuYW1lcyhzb2JqX2hmc2MpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHllcyA9ICJIRlNDIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBubyA9IHNvYmpfdHJhaiRjbHVzdGVyX3R5cGUpICU+JQogIGFzLmZhY3RvcigpCmBgYAoKIyMgRmlndXJlcwoKQ2VsbHMgb24gdGhlIGZ1bGwgYXRsYXMgOgoKYGBge3IgZmlnX3RyYWpfbG9jYXRpb24sIGZpZy53aWR0aCA9IDIsIGZpZy5oZWlnaHQgPSAyfQpzb2JqJGNlbGxfYmMgPSBjb2xuYW1lcyhzb2JqKQpzb2JqX3RyYWokY2VsbF9iYyA9IGNvbG5hbWVzKHNvYmpfdHJhaikKc29iaiRpc190cmFqID0gZHBseXI6OmxlZnRfam9pbihzb2JqQG1ldGEuZGF0YVssIGMoImNlbGxfYmMiLCAicGVyY2VudC5tdCIpXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzb2JqX3RyYWpAbWV0YS5kYXRhWywgYygiY2VsbF9iYyIsICJjbHVzdGVyX3R5cGUiKV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnkgPSAiY2VsbF9iYyIpWywgImNsdXN0ZXJfdHlwZSJdCgpTZXVyYXQ6OkRpbVBsb3Qoc29iaiwgcmVkdWN0aW9uID0gbmFtZTJEX2F0bGFzLCBwdC5zaXplID0gMC4wMDAwMDEsCiAgICAgICAgICAgICAgICBncm91cC5ieSA9ICJpc190cmFqIiwgb3JkZXIgPSBsZXZlbHMoc29ial90cmFqJGNsdXN0ZXJfdHlwZSkpICsKICBnZ3Bsb3QyOjpzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYyhjb2xvcl9tYXJrZXJzW2MoIklCTCIsICJPUlMiLCAiSEZTQyIpXSwgYmdfY29sb3IpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBjKCJJQkwiLCAiT1JTIiwgIkhGU0MiLCBOQSksIG5hLnZhbHVlID0gYmdfY29sb3IpICsKICBnZ3Bsb3QyOjp0aGVtZShhc3BlY3QucmF0aW8gPSAxKSArCiAgU2V1cmF0OjpOb0F4ZXMoKSArIFNldXJhdDo6Tm9MZWdlbmQoKQpgYGAKCgpQcm9qZWN0IG5hbWUgOgoKYGBge3IgZmlnX3RyYWpfcHJvamVjdF9uYW1lLCBmaWcud2lkdGggPSA0LCBmaWcuaGVpZ2h0ID0gNH0KIyBSYW5kb20gb3JkZXIKc2V0LnNlZWQoMTIzNCkKcm5kX29yZGVyID0gc2FtcGxlKGNvbG5hbWVzKHNvYmpfdHJhaiksIHJlcGxhY2UgPSBGQUxTRSwgc2l6ZSA9IG5jb2woc29ial90cmFqKSkKCiMgRXh0cmFjdCBjb29yZGluYXRlcwpjZWxsc19jb29yZCA9IHNvYmpfdHJhakByZWR1Y3Rpb25zW1tuYW1lMkRdXUBjZWxsLmVtYmVkZGluZ3MgJT4lCiAgYXMuZGF0YS5mcmFtZSgpICU+JQogIGBjb2xuYW1lczwtYChjKCJEaW0xIiwgIkRpbTIiKSkKY2VsbHNfY29vcmQkc2FtcGxlX3R5cGUgPSBzb2JqX3RyYWokc2FtcGxlX3R5cGUKY2VsbHNfY29vcmQgPSBjZWxsc19jb29yZFtvcmRlcihzb2JqX3RyYWokc2FtcGxlX3R5cGUpLCBdCgojIFBsb3QKZ2dwbG90Mjo6Z2dwbG90KGNlbGxzX2Nvb3JkLCBhZXMoeCA9IERpbTEsIHkgPSBEaW0yLCBjb2wgPSBzYW1wbGVfdHlwZSkpICsKICBnZ3Bsb3QyOjpnZW9tX3BvaW50KHNpemUgPSAxLjIpICsKICBnZ3Bsb3QyOjpzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gc2FtcGxlX3R5cGVfY29sb3JzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBuYW1lcyhzYW1wbGVfdHlwZV9jb2xvcnMpKSArCiAgZ2dwbG90Mjo6dGhlbWVfdm9pZCgpICsKICBnZ3Bsb3QyOjp0aGVtZShhc3BlY3QucmF0aW8gPSAxLAogICAgICAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKYGBgCgoKQ2x1c3RlciB0eXBlIDoKCmBgYHtyIGZpZ190cmFqX2NsdXN0ZXJfdHlwZSwgZmlnLndpZHRoID0gMywgZmlnLmhlaWdodCA9IDN9ClNldXJhdDo6RGltUGxvdChzb2JqX3RyYWosIHJlZHVjdGlvbiA9IG5hbWUyRCwgcHQuc2l6ZSA9IDAuNSwKICAgICAgICAgICAgICAgIGdyb3VwLmJ5ID0gImNsdXN0ZXJfdHlwZSIsIGNvbHMgPSBjb2xvcl9tYXJrZXJzKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXNwZWN0LnJhdGlvID0gMSkgKwogIFNldXJhdDo6Tm9BeGVzKCkgKwogIFNldXJhdDo6Tm9MZWdlbmQoKQpgYGAKClBzZXVkb3RpbWUgOgoKYGBge3IgZmlnX3RyYWpfcHNldWRvdGltZSwgZmlnLndpZHRoID0gNiwgZmlnLmhlaWdodCA9IDR9ClNldXJhdDo6RmVhdHVyZVBsb3Qoc29ial90cmFqLCByZWR1Y3Rpb24gPSBuYW1lMkQsIHB0LnNpemUgPSAwLjUsCiAgICAgICAgICAgICAgICAgICAgZmVhdHVyZXMgPSAicHNldWRvdGltZSIpICsKICBnZ3Bsb3QyOjpzY2FsZV9jb2xvcl9ncmFkaWVudG4oY29sb3JzID0gdmlyaWRpczo6dmlyaWRpcyhuID0gMTAwKSkgKwogIGdncGxvdDI6OmxpbXMoeCA9IHJhbmdlKHNvYmpfdHJhakByZWR1Y3Rpb25zW1tuYW1lMkRdXUBjZWxsLmVtYmVkZGluZ3NbLCAxXSksCiAgICAgICAgICAgICAgICB5ID0gcmFuZ2Uoc29ial90cmFqQHJlZHVjdGlvbnNbW25hbWUyRF1dQGNlbGwuZW1iZWRkaW5nc1ssIDJdKSkgKwogIGdncGxvdDI6OnRoZW1lKGFzcGVjdC5yYXRpbyA9IDEsCiAgICAgICAgICAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKwogIFNldXJhdDo6Tm9BeGVzKCkKYGBgCgpQc2V1ZG90aW1lIHdpdGggZHlucGxvdCdzIGZ1bmN0aW9uIDoKCmBgYHtyIGZpZ190cmFqX3BzZXVkb3RpbWVfZHlucGxvdCwgZmlnLndpZHRoID0gNSwgZmlnLmhlaWdodCA9IDV9CmR5bnBsb3Q6OnBsb3RfZGltcmVkKHRyYWplY3RvcnkgPSBteV90cmFqLAogICAgICAgICAgICAgICAgICAgICBkaW1yZWQgPSBzb2JqX3RyYWpbW25hbWUyRF1dQGNlbGwuZW1iZWRkaW5ncywKICAgICAgICAgICAgICAgICAgICAgIyBDZWxscwogICAgICAgICAgICAgICAgICAgICBjb2xvcl9jZWxscyA9ICdwc2V1ZG90aW1lJywKICAgICAgICAgICAgICAgICAgICAgc2l6ZV9jZWxscyA9IDEuNiwKICAgICAgICAgICAgICAgICAgICAgYm9yZGVyX3JhZGl1c19wZXJjZW50YWdlID0gMCwKICAgICAgICAgICAgICAgICAgICAgIyBUcmFqZWN0b3J5CiAgICAgICAgICAgICAgICAgICAgIHBsb3RfdHJhamVjdG9yeSA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgIGNvbG9yX3RyYWplY3RvcnkgPSAibm9uZSIsCiAgICAgICAgICAgICAgICAgICAgIGxhYmVsX21pbGVzdG9uZXMgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgc2l6ZV9taWxlc3RvbmVzID0gMCwKICAgICAgICAgICAgICAgICAgICAgc2l6ZV90cmFuc2l0aW9ucyA9IDEpCmBgYAoKIyBPRVAwMDIzMjEgZGF0YXNldAoKIyMgU2V0dGluZ3MKCldlIGxvYWQgdGhlIGRhdGFzZXQgY29udGFpbmluZyBhbGwgY2VsbHMgOgoKYGBge3IgbG9hZF9zb2JqX3d1fQpzb2JqID0gcmVhZFJEUyhwYXN0ZTAoZGF0YV9kaXIsICIvNV93dS8zX2NvbWJpbmVkL3d1X3NvYmoucmRzIikpCnNvYmoKYGBgCgpUaGlzIGlzIHRoZSBwcm9qZWN0aW9uIG5hbWUgdG8gdmlzdWFsaXplIGNlbGxzIDoKCmBgYHtyIGFsbF9zb2JqX25hbWUyRF93dX0KbmFtZTJEID0gImhhcm1vbnlfMzhfdHNuZSIKbmFtZTJEX2F0bGFzID0gbmFtZTJECmBgYAoKVGhlc2UgYXJlIGFsbCB0aGUgc2FtcGxlcyBhbmFseXplZCA6CgpgYGB7ciBzYW1wbGVfaW5mb193dSwgY2xhc3Muc291cmNlID0gJ2ZvbGQtaGlkZScsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSAzfQpzYW1wbGVfaW5mbyA9IHJlYWRSRFMocGFzdGUwKGRhdGFfZGlyLCAiLzVfd3UvMV9tZXRhZGF0YS93dV9zYW1wbGVfaW5mby5yZHMiKSkKCiMgTmIgY2VsbHMgYnkgZGF0YXNldAp0b19wbG90ID0gdGFibGUoc29iaiRzYW1wbGVfaWRlbnRpZmllcikgJT4lCiAgYXMuZGF0YS5mcmFtZS50YWJsZSguLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpICU+JQogIGBjb2xuYW1lczwtYChjKCJzYW1wbGVfaWRlbnRpZmllciIsICJuYl9jZWxscyIpKSAlPiUKICBkcGx5cjo6bGVmdF9qb2luKHggPSAuLCB5ID0gc2FtcGxlX2luZm8sIGJ5ID0gInNhbXBsZV9pZGVudGlmaWVyIikgCgojIHBhdGNod29yawpwbG90X2xpc3QgPSBhcXVhcml1czo6ZmlnX3Bsb3RfZ2IodG9fcGxvdCwgdGl0bGUgPSAiQXZhaWxhYmxlIGRhdGFzZXRzIikKcGF0Y2h3b3JrOjp3cmFwX3Bsb3RzKHBsb3RfbGlzdCkgKwogIHBhdGNod29yazo6cGxvdF9sYXlvdXQoZGVzaWduID0gIkFcbkIiLCBoZWlnaHRzID0gYygwLjEsNSkpICYKICBnZ3Bsb3QyOjp0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBmYWNlID0gImJvbGQiLCBzaXplID0gMTUpKQpgYGAKCldlIGRlZmluZSBjbHVzdGVyIHR5cGUgYW5kIGNsdXN0ZXIgZmFtaWx5IDoKCmBgYHtyIGNsdXN0ZXJfdHlwZV9mYW1pbHlfd3UsIGZpZy53aWR0aCA9IDcsIGZpZy5oZWlnaHQgPSA1fQpzb2JqJGNlbGxfdHlwZSA9IHNvYmokY2VsbF90eXBlICU+JQogIGFzLmNoYXJhY3RlcigpICU+JQogIGZhY3RvciguLCBsZXZlbHMgPSBuYW1lcyhjb2xvcl9tYXJrZXJzKSkKCmNsdXN0ZXJfdHlwZSA9IHRhYmxlKHNvYmokY2VsbF90eXBlLCBzb2JqJHNldXJhdF9jbHVzdGVycykgJT4lCiAgcHJvcC50YWJsZSguLCBtYXJnaW4gPSAyKSAlPiUKICBhcHBseSguLCAyLCB3aGljaC5tYXgpCmNsdXN0ZXJfdHlwZSA9IHNldE5hbWVzKG5tID0gbmFtZXMoY2x1c3Rlcl90eXBlKSwKICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzKHNvYmokY2VsbF90eXBlKVtjbHVzdGVyX3R5cGVdKQoKc29iaiRjbHVzdGVyX3R5cGUgPSBjbHVzdGVyX3R5cGVbc29iaiRzZXVyYXRfY2x1c3RlcnNdCnNvYmokY2x1c3Rlcl90eXBlID0gZmFjdG9yKHNvYmokY2x1c3Rlcl90eXBlLAogICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBsZXZlbHMoc29iaiRjZWxsX3R5cGUpKSAlPiUKICBiYXNlOjpkcm9wbGV2ZWxzKCkKc29iaiRjbHVzdGVyX2ZhbWlseSA9IGN1c3RvbV9vcmRlcl9jZWxsX3R5cGVbc29iaiRjbHVzdGVyX3R5cGUsICJjZWxsX2ZhbWlseSJdCnNvYmokY2x1c3Rlcl9mYW1pbHkgPSBmYWN0b3Ioc29iaiRjbHVzdGVyX2ZhbWlseSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBuYW1lcyhmYW1pbHlfY29sb3IpKQpgYGAKCgojIyBHbG9iYWwgZmlndXJlcwoKUHJvamVjdCBuYW1lIDoKCmBgYHtyIGZpZ3MyX3NhbXBsZV9pZGVudGlmaWVyLCBmaWcud2lkdGggPSA0LCBmaWcuaGVpZ2h0ID0gNH0KIyBSYW5kb20gb3JkZXIKc2V0LnNlZWQoMTIzNCkKcm5kX29yZGVyID0gc2FtcGxlKGNvbG5hbWVzKHNvYmopLCByZXBsYWNlID0gRkFMU0UsIHNpemUgPSBuY29sKHNvYmopKQoKIyBFeHRyYWN0IGNvb3JkaW5hdGVzCmNlbGxzX2Nvb3JkID0gc29iakByZWR1Y3Rpb25zW1tuYW1lMkRfYXRsYXNdXUBjZWxsLmVtYmVkZGluZ3MgJT4lCiAgYXMuZGF0YS5mcmFtZSgpICU+JQogIGBjb2xuYW1lczwtYChjKCJEaW0xIiwgIkRpbTIiKSkKY2VsbHNfY29vcmQkcHJvamVjdF9uYW1lID0gc29iaiRwcm9qZWN0X25hbWUKY2VsbHNfY29vcmQgPSBjZWxsc19jb29yZFsocm5kX29yZGVyKSwgXQoKIyBQbG90CmdncGxvdDI6OmdncGxvdChjZWxsc19jb29yZCwgYWVzKHggPSBEaW0xLCB5ID0gRGltMiwgY29sID0gcHJvamVjdF9uYW1lKSkgKwogIGdncGxvdDI6Omdlb21fcG9pbnQoc2l6ZSA9IDAuNSkgKwogIGdncGxvdDI6OnNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBzYW1wbGVfaW5mbyRjb2xvciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gc2FtcGxlX2luZm8kcHJvamVjdF9uYW1lKSArCiAgZ2dwbG90Mjo6dGhlbWVfdm9pZCgpICsKICBnZ3Bsb3QyOjp0aGVtZShhc3BlY3QucmF0aW8gPSAxLAogICAgICAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKYGBgCgpDZWxsIHR5cGUgYW5ub3RhdGlvbiA6CgpgYGB7ciBmaWdzMl9jZWxsX3R5cGUsIGZpZy53aWR0aCA9IDQsIGZpZy5oZWlnaHQgPSA0fQpTZXVyYXQ6OkRpbVBsb3Qoc29iaiwgcmVkdWN0aW9uID0gbmFtZTJELCBwdC5zaXplID0gMC41LAogICAgICAgICAgICAgICAgZ3JvdXAuYnkgPSAiY2VsbF90eXBlIiwgY29scyA9IGNvbG9yX21hcmtlcnMpICsKICBnZ3Bsb3QyOjp0aGVtZShhc3BlY3QucmF0aW8gPSAxKSArCiAgU2V1cmF0OjpOb0F4ZXMoKSArCiAgU2V1cmF0OjpOb0xlZ2VuZCgpCmBgYAoKQ2x1c3RlciB0eXBlIGFubm90YXRpb24gOgoKYGBge3IgZmlnczJfY2x1c3Rlcl90eXBlLCBmaWcud2lkdGggPSA0LCBmaWcuaGVpZ2h0ID0gNH0KU2V1cmF0OjpEaW1QbG90KHNvYmosIHJlZHVjdGlvbiA9IG5hbWUyRCwgcHQuc2l6ZSA9IDAuNSwKICAgICAgICAgICAgICAgIGdyb3VwLmJ5ID0gImNsdXN0ZXJfdHlwZSIsIGNvbHMgPSBjb2xvcl9tYXJrZXJzKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXNwZWN0LnJhdGlvID0gMSkgKwogIFNldXJhdDo6Tm9BeGVzKCkgKwogIFNldXJhdDo6Tm9MZWdlbmQoKQpgYGAKCkdlbmUgZXhwcmVzc2lvbiB0byBhc3Nlc3MgYW5ub3RhdGlvbiA6CgpgYGB7ciBmaWdzMl9nZW5lX2ZhbWlseSwgZmlnLndpZHRoID0gNCwgZmlnLmhlaWdodCA9IDR9CmdlbmVzID0gYygiUFRQUkMiLCAiTVNYMiIsICJLUlQxNCIpCm5hbWVzKGdlbmVzKSA9IGMoImltbXVuZSBjZWxscyIsICJtYXRyaXggY2VsbHMiLCAibm9uLW1hdHJpeCBjZWxscyIpCgpwbG90X2xpc3QgPSBsYXBwbHkoYygxOmxlbmd0aChnZW5lcykpLCBGVU4gPSBmdW5jdGlvbihnZW5lX2lkKSB7CiAgZ2VuZSA9IGdlbmVzW1tnZW5lX2lkXV0KICBwb3AgPSBuYW1lcyhnZW5lcylbZ2VuZV9pZF0KICAKICBzb2JqJG15X2dlbmUgPSBTZXVyYXQ6OkZldGNoRGF0YShzb2JqLCBnZW5lKVssIDFdICU+JQogICAgYXF1YXJpdXM6OnJ1bl9yZXNjYWxlKC4sIG5ld19taW4gPSAwLCBuZXdfbWF4ID0gMTApCiAgCiAgU2V1cmF0OjpGZWF0dXJlUGxvdChzb2JqLCBmZWF0dXJlcyA9ICJteV9nZW5lIiwgcmVkdWN0aW9uID0gbmFtZTJEX2F0bGFzKSArCiAgICBnZ3Bsb3QyOjpzY2FsZV9jb2xvcl9ncmFkaWVudG4oY29sb3JzID0gYXF1YXJpdXM6OmNvbG9yX2dlbmUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gc2VxKDAsIDEwLCBieSA9IDIuNSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygibWluIiwgcmVwKCIiLCAzKSwgIm1heCIpKSArCiAgICBnZ3Bsb3QyOjpsYWJzKHRpdGxlID0gZ2VuZSkgKyAKICAgICMgc3VidGl0bGUgPSBwb3ApICsKICAgIGdncGxvdDI6OnRoZW1lKGFzcGVjdC5yYXRpbyA9IDEsCiAgICAgICAgICAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBzaXplID0gMTcpLAogICAgICAgICAgICAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgc2l6ZSA9IDE1KSwKICAgICAgICAgICAgICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNSksCiAgICAgICAgICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsKICAgIFNldXJhdDo6Tm9BeGVzKCkKfSkKCnBsb3RfbGlzdApgYGAKCkRvdHBsb3QgOgoKYGBge3IgZmlnczJfZG90cGxvdCwgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDUsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUifQpjdXN0b21fb3JkZXJfY2VsbF90eXBlID0gY3VzdG9tX29yZGVyX2NlbGxfdHlwZVtsZXZlbHMoc29iaiRjbHVzdGVyX3R5cGUpLCBjKCJjZWxsX3R5cGUiLCAiY2VsbF9mYW1pbHkiKV0KCnBsb3RfbGlzdCA9IFNldXJhdDo6RG90UGxvdChzb2JqLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgZmVhdHVyZXMgPSBjKCJQVFBSQyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNEM0UiLCAiQ0Q0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ0QyMDciLCAiQUlGMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyAiUFJETTEiLCAiS1JUODUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJNU1gyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiS1JUMzIiLCAiS1JUMzUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJLUlQzMSIsICJQUlI5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQkFNQkkiLCAiQUxESDFBMyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIktSVDcxIiwgIktSVDczIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVE9QMkEiLCAiTUNNNSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIktSVDE0IiwgIkNYQ0wxNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIktSVDE1IiwgIkNPTDE3QTEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJESU8yIiwgIlRDRUFMMiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIktSVDE2IiwgIktSVDZDIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiU1BJTks1IiwgIkxZNkQiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwLmJ5ID0gImNsdXN0ZXJfdHlwZSIsIHNjYWxlID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNjYWxlLmJ5ID0gInJhZGl1cyIsIHNjYWxlLm1pbiA9IE5BLCBzY2FsZS5tYXggPSBOQSkgKwogIGdncGxvdDI6OnNjYWxlX2NvbG9yX2dyYWRpZW50bihjb2xvcnMgPSBhcXVhcml1czo6Y29sb3JfZ2VuZSkgKwogIGdncGxvdDI6OnRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLAogICAgICAgICAgICAgICAgIGxlZ2VuZC5kaXJlY3Rpb24gPSAidmVydGljYWwiLAogICAgICAgICAgICAgICAgICMgbGVnZW5kLmp1c3RpZmljYXRpb24gPSAiYm90dG9tIiwKICAgICAgICAgICAgICAgICBsZWdlbmQuYm94ID0gImhvcml6b250YWwiLAogICAgICAgICAgICAgICAgIGxlZ2VuZC5ib3gubWFyZ2luID0gbWFyZ2luKDAsMjUsMCwwKSwKICAgICAgICAgICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgIGF4aXMudGlja3MueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICBheGlzLmxpbmUueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEsIHNpemUgPSAxMSwgY29sb3IgPSAiYmxhY2siKSwKICAgICAgICAgICAgICAgICBwbG90Lm1hcmdpbiA9IHVuaXQoYygwLDAuNSwwLDApLCAiY20iKSkKCnAgPSBnZ3Bsb3QyOjpnZ3Bsb3QoY3VzdG9tX29yZGVyX2NlbGxfdHlwZSwgYWVzKHkgPSBjZWxsX3R5cGUsIHggPSAwKSkgKwogIGdncGxvdDI6Omdlb21fcG9pbnQoc2l6ZSA9IDApICsKICBnZ3Bsb3QyOjpnZW9tX3NlZ21lbnQoYWVzKHkgPSAwLjUsIHllbmQgPSAyLjUsIHggPSAwLCB4ZW5kID0gMCksIHNpemUgPSA2LCBjb2wgPSBmYW1pbHlfY29sb3JbImltbXVuZSBjZWxscyJdKSArCiAgZ2dwbG90Mjo6Z2VvbV9zZWdtZW50KGFlcyh5ID0gMi41LCB5ZW5kID0gNy41LCB4ID0gMCwgeGVuZCA9IDApLCBzaXplID0gNiwgY29sID0gZmFtaWx5X2NvbG9yWyJtYXRyaXgiXSkgKwogIGdncGxvdDI6Omdlb21fc2VnbWVudChhZXMoeSA9IDcuNSwgeWVuZCA9IDExLjUsIHggPSAwLCB4ZW5kID0gMCksIHNpemUgPSA2LCBjb2wgPSBmYW1pbHlfY29sb3JbIm5vbiBtYXRyaXgiXSkgKwogIGdncGxvdDI6OnNjYWxlX3hfY29udGludW91cyhleHBhbmQgPSBjKDAsMCksIGxpbWl0cyA9IGMoMCwwKSkgKwogIGdncGxvdDI6OnRoZW1lX2NsYXNzaWMoKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgYXhpcy50aWNrcy54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgYXhpcy5saW5lLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBjb2xvciA9ICJibGFjayIpLAogICAgICAgICAgICAgICAgIHBsb3QubWFyZ2luID0gdW5pdChjKDAuNSwwLDAsMC41KSwgImNtIikpCgpwbG90X2xpc3QgPSBwYXRjaHdvcms6OndyYXBfcGxvdHMocCwgcGxvdF9saXN0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbnJvdyA9IDEsIHdpZHRocyA9IGMoMSwgMjUpKQpwbG90X2xpc3QKYGBgCgojIyBJQkwgYW5kIE9SUyBkYXRhc2V0CgpXZSBsb2FkIHRoZSBJQkwgKyBPUlMgZGF0YXNldCA6CgpgYGB7ciBzb2JqX2libG9yc193dX0Kc29ial9pYmxvcnMgPSByZWFkUkRTKHBhc3RlMChkYXRhX2RpciwgIi81X3d1LzRfaWJsX29ycy9pYmxtb3JzX3NvYmoucmRzIikpCnNvYmpfaWJsb3JzCmBgYAoKClRoaXMgaXMgdGhlIHByb2plY3Rpb24gbmFtZSB0byB2aXN1YWxpemUgY2VsbHMgOgoKYGBge3Igc29ial9uYW1lMkRfaWJsb3JzX3d1fQpuYW1lMkQgPSAiaGFybW9ueV8yMF90c25lIgpgYGAKClRvIHJlcHJlc2VudCByZXN1bHRzIGZyb20gZGlmZmVyZW50aWFsIGV4cHJlc3Npb24sIHdlIGxvYWQgdGhlIGFuYWx5c2VzIHJlc3VsdHMgOgoKYGBge3IgbGlzdF9yZXN1bHRzX2libG9yc193dX0KbGlzdF9yZXN1bHRzID0gcmVhZFJEUyhwYXN0ZTAoZGF0YV9kaXIsICIvNV93dS80X2libF9vcnMvaWJsbW9yc19saXN0X3Jlc3VsdHMucmRzIikpCgpsYXBwbHkobGlzdF9yZXN1bHRzLCBGVU4gPSBuYW1lcykKYGBgCgpXZSBkZWZpbmVkIGNsdXN0ZXIgdHlwZSA6CgpgYGB7ciBjbHVzdGVyX3R5cGVfaWJsb3JzX3d1LCBmaWcud2lkdGggPSA3LCBmaWcuaGVpZ2h0ID0gNX0KY2x1c3Rlcl90eXBlID0gdGFibGUoc29ial9pYmxvcnMkY2VsbF90eXBlLCBzb2JqX2libG9ycyRzZXVyYXRfY2x1c3RlcnMpICU+JQogIHByb3AudGFibGUoLiwgbWFyZ2luID0gMikgJT4lCiAgYXBwbHkoLiwgMiwgd2hpY2gubWF4KQpjbHVzdGVyX3R5cGUgPSBzZXROYW1lcyhubSA9IG5hbWVzKGNsdXN0ZXJfdHlwZSksCiAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyhzb2JqX2libG9ycyRjZWxsX3R5cGUpW2NsdXN0ZXJfdHlwZV0pCgpzb2JqX2libG9ycyRjbHVzdGVyX3R5cGUgPSBjbHVzdGVyX3R5cGVbc29ial9pYmxvcnMkc2V1cmF0X2NsdXN0ZXJzXQpzb2JqX2libG9ycyRjbHVzdGVyX3R5cGUgPSBmYWN0b3Ioc29ial9pYmxvcnMkY2x1c3Rlcl90eXBlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gYygiSUJMIiwgIk9SUyIpKQpgYGAKCiMjIElCTCArIE9SUyBmaWd1cmUKCklCTCArIE9SUyBvbiB0aGUgZnVsbCBhdGxhcyA6CgpgYGB7ciBmaWdzMl9pYmxvcnNfbG9jYXRpb24sIGZpZy53aWR0aCA9IDEuNSwgZmlnLmhlaWdodCA9IDEuNX0Kc29iaiRjZWxsX2JjID0gY29sbmFtZXMoc29iaikKc29ial9pYmxvcnMkY2VsbF9iYyA9IGNvbG5hbWVzKHNvYmpfaWJsb3JzKQpzb2JqJGlzX2libG9ycyA9IGRwbHlyOjpsZWZ0X2pvaW4oc29iakBtZXRhLmRhdGFbLCBjKCJjZWxsX2JjIiwgInBlcmNlbnQubXQiKV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzb2JqX2libG9yc0BtZXRhLmRhdGFbLCBjKCJjZWxsX2JjIiwgImNsdXN0ZXJfdHlwZSIpXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJ5ID0gImNlbGxfYmMiKVssICJjbHVzdGVyX3R5cGUiXQoKU2V1cmF0OjpEaW1QbG90KHNvYmosIHJlZHVjdGlvbiA9IG5hbWUyRF9hdGxhcywgcHQuc2l6ZSA9IDAuMDAwMDAxLAogICAgICAgICAgICAgICAgZ3JvdXAuYnkgPSAiaXNfaWJsb3JzIiwgb3JkZXIgPSBGQUxTRSkgKwogIGdncGxvdDI6OnNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKGNvbG9yX21hcmtlcnNbYygiSUJMIiwgIk9SUyIpXSwgYmdfY29sb3IpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBjKCJJQkwiLCAiT1JTIiwgTkEpLCBuYS52YWx1ZSA9IGJnX2NvbG9yKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXNwZWN0LnJhdGlvID0gMSwKICAgICAgICAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArCiAgU2V1cmF0OjpOb0F4ZXMoKSArIFNldXJhdDo6Tm9MZWdlbmQoKQpgYGAKCkNsdXN0ZXIgdHlwZSA6CgpgYGB7ciBmaWdzMl9pYmxvcnNfY2x1c3Rlcl90eXBlLCBmaWcud2lkdGggPSA0LCBmaWcuaGVpZ2h0ID0gNH0KU2V1cmF0OjpEaW1QbG90KHNvYmpfaWJsb3JzLCByZWR1Y3Rpb24gPSBuYW1lMkQsIHB0LnNpemUgPSAxLAogICAgICAgICAgICAgICAgZ3JvdXAuYnkgPSAiY2x1c3Rlcl90eXBlIiwgY29scyA9IGNvbG9yX21hcmtlcnMpICsKICBnZ3Bsb3QyOjp0aGVtZShhc3BlY3QucmF0aW8gPSAxKSArCiAgU2V1cmF0OjpOb0F4ZXMoKSArCiAgU2V1cmF0OjpOb0xlZ2VuZCgpCmBgYAoKCkRFIGdlbmVzIGJldHdlZW4gSUJMIGFuZCBPUlMgOgoKYGBge3IgZmlnczJfaWJsb3JzX2RlX3BvcCwgZmlnLndpZHRoID0gNiwgZmlnLmhlaWdodCA9IDZ9Cm1hcmsgPSBsaXN0X3Jlc3VsdHMkSUJMX3ZzX09SUyRtYXJrCm1hcmskZ2VuZV9uYW1lID0gcm93bmFtZXMobWFyaykKbWFya19sYWJlbCA9IHJiaW5kKAogICMgdXAtcmVndWxhdGVkIGluIElCTAogIG1hcmsgJT4lIGRwbHlyOjp0b3BfbiguLCBuID0gMjAsIHd0ID0gYXZnX2xvZ0ZDKSwKICAjIHVwLXJlZ3VsYXRlZCBpbiBPUlMKICBtYXJrICU+JSBkcGx5cjo6dG9wX24oLiwgbiA9IDIwLCB3dCA9IC1hdmdfbG9nRkMpLAogICMgcmVwcmVzZW50YXRpdmUgYW5kIHNlbGVjdGl2ZSBmb3IgSUJMCiAgbWFyayAlPiUgZHBseXI6OnRvcF9uKC4sIG4gPSAyMCwgd3QgPSAocGN0LjEgLSBwY3QuMikpLAogICMgcmVwcmVzZW50YXRpdmUgYW5kIHNlbGVjdGl2ZSBmb3IgT1JTCiAgbWFyayAlPiUgZHBseXI6OnRvcF9uKC4sIG4gPSAyMCwgd3QgPSAtKHBjdC4xIC0gcGN0LjIpKSkgJT4lCiAgZHBseXI6OmRpc3RpbmN0KCkKbWFya19sYWJlbCA9IG1hcmtfbGFiZWxbIWdyZXBsKHJvd25hbWVzKG1hcmtfbGFiZWwpLCBwYXR0ZXJuID0gIl5NVCIpLCBdCgphdmdfbG9nRkNfcmFuZ2UgPSBzZXROYW1lcyhjKG1pbihtYXJrX2xhYmVsJGF2Z19sb2dGQyksIC0xLCAwLCAxLCBtYXgobWFya19sYWJlbCRhdmdfbG9nRkMpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgbm0gPSBjKCJkb2RnZXJibHVlNCIsICJkb2RnZXJibHVlMyIsICIjQjdCN0I3IiwgImZpcmVicmljazMiLCAiZmlyZWJyaWNrNCIpKQoKCmdncGxvdDI6OmdncGxvdChtYXJrLCBhZXMoeCA9IHBjdC4xLCB5ID0gcGN0LjIsIGNvbCA9IGF2Z19sb2dGQykpICsKICBnZ3Bsb3QyOjpnZW9tX2FibGluZShzbG9wZSA9IDEsIGludGVyY2VwdCA9IDAsIGx0eSA9IDIpICsKICBnZ3Bsb3QyOjpnZW9tX3BvaW50KCkgKwogIGdncmVwZWw6Omdlb21fbGFiZWxfcmVwZWwoZGF0YSA9IG1hcmtfbGFiZWwsIG1heC5vdmVybGFwcyA9IEluZiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFlcyh4ID0gcGN0LjEsIHkgPSBwY3QuMiwgbGFiZWwgPSBnZW5lX25hbWUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sID0gImJsYWNrIiwgZmlsbCA9IE5BLCBzaXplID0gMy41LCBsYWJlbC5zaXplID0gTkEpICsKICBnZ3Bsb3QyOjpsYWJzKHggPSAiRW5yaWNoZWQgaW4gSUJMIiwKICAgICAgICAgICAgICAgIHkgPSAiRW5yaWNoZWQgaW4gT1JTIikgKwogIGdncGxvdDI6OnNjYWxlX2NvbG9yX2dyYWRpZW50bihjb2xvcnMgPSBuYW1lcyhhdmdfbG9nRkNfcmFuZ2UpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZXMgPSBzY2FsZXM6OnJlc2NhbGUodW5uYW1lKGF2Z19sb2dGQ19yYW5nZSkpKSArCiAgZ2dwbG90Mjo6dGhlbWVfY2xhc3NpYygpICsKICBnZ3Bsb3QyOjp0aGVtZShhc3BlY3QucmF0aW8gPSAxKQpgYGAKCkdTRUEgcGxvdCA6CgpgYGB7ciBmaWdzMl9pYmxvcnNfa2VyYXRpbml6YXRpb24sIGZpZy53aWR0aCA9IDYsIGZpZy5oZWlnaHQgPSA0fQp0aGVfZ3NfbmFtZSA9ICJSRUFDVE9NRV9LRVJBVElOSVpBVElPTiIgCnRoZV9jb250ZW50ID0gbGlzdF9yZXN1bHRzJElCTF92c19PUlMkZ3NlYUByZXN1bHQgJT4lCiAgZHBseXI6OmZpbHRlcihJRCA9PSB0aGVfZ3NfbmFtZSkKdGhlX3N1YnRpdGxlID0gcGFzdGUwKCJcbk5FUyA6ICIsIHJvdW5kKHRoZV9jb250ZW50JE5FUywgMiksCiAgICAgICAgICAgICAgICAgICAgICAiIHwgcHZhbHVlIDogIiwgcm91bmQodGhlX2NvbnRlbnQkcHZhbHVlLCA0KSwKICAgICAgICAgICAgICAgICAgICAgICIgfCBzZXQgc2l6ZSA6ICIsIHRoZV9jb250ZW50JHNldFNpemUsICIgZ2VuZXMiKQoKZW5yaWNocGxvdDo6Z3NlYXBsb3QyKHggPSBsaXN0X3Jlc3VsdHMkSUJMX3ZzX09SUyRnc2VhLAogICAgICAgICAgICAgICAgICAgICAgZ2VuZVNldElEID0gdGhlX2dzX25hbWUpICsKICBnZ3Bsb3QyOjpsYWJzKHRpdGxlID0gdGhlX2dzX25hbWUsCiAgICAgICAgICAgICAgICBzdWJ0aXRsZSA9IHRoZV9zdWJ0aXRsZSkgKwogIGdncGxvdDI6OnRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXJnaW4gPSBnZ3Bsb3QyOjptYXJnaW4oMywgMywgNSwgMykpLAogICAgICAgICAgICAgICAgIHBsb3Quc3VidGl0bGUgPSBnZ3RleHQ6OmVsZW1lbnRfbWFya2Rvd24oaGp1c3QgPSAwLjUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gMTApKQpgYGAKCmBgYHtyIGZpZ3MyX2libG9yc19pZm5hLCBmaWcud2lkdGggPSA2LCBmaWcuaGVpZ2h0ID0gNH0KdGhlX2dzX25hbWUgPSAiSEFMTE1BUktfSU5URVJGRVJPTl9HQU1NQV9SRVNQT05TRSIgCnRoZV9jb250ZW50ID0gbGlzdF9yZXN1bHRzJElCTF92c19PUlMkZ3NlYUByZXN1bHQgJT4lCiAgZHBseXI6OmZpbHRlcihJRCA9PSB0aGVfZ3NfbmFtZSkKdGhlX3N1YnRpdGxlID0gcGFzdGUwKCJcbk5FUyA6ICIsIHJvdW5kKHRoZV9jb250ZW50JE5FUywgMiksCiAgICAgICAgICAgICAgICAgICAgICAiIHwgcHZhbHVlIDogIiwgcm91bmQodGhlX2NvbnRlbnQkcHZhbHVlLCA0KSwKICAgICAgICAgICAgICAgICAgICAgICIgfCBzZXQgc2l6ZSA6ICIsIHRoZV9jb250ZW50JHNldFNpemUsICIgZ2VuZXMiKQoKZW5yaWNocGxvdDo6Z3NlYXBsb3QyKHggPSBsaXN0X3Jlc3VsdHMkSUJMX3ZzX09SUyRnc2VhLAogICAgICAgICAgICAgICAgICAgICAgZ2VuZVNldElEID0gdGhlX2dzX25hbWUpICsKICBnZ3Bsb3QyOjpsYWJzKHRpdGxlID0gdGhlX2dzX25hbWUsCiAgICAgICAgICAgICAgICBzdWJ0aXRsZSA9IHRoZV9zdWJ0aXRsZSkgKwogIGdncGxvdDI6OnRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXJnaW4gPSBnZ3Bsb3QyOjptYXJnaW4oMywgMywgNSwgMykpLAogICAgICAgICAgICAgICAgIHBsb3Quc3VidGl0bGUgPSBnZ3RleHQ6OmVsZW1lbnRfbWFya2Rvd24oaGp1c3QgPSAwLjUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gMTApKQpgYGAKCkdlbmVzIG9mIGludGVyZXN0IDoKCmBgYHtyIGZpZ3MyX2libG9yc19nZW5lcywgZmlnLndpZHRoID0gMiwgZmlnLmhlaWdodCA9IDJ9CmdlbmVzID0gYygiS1JUMTYiLCAiQ09MMTdBMSIsICJEU1QiLCAiS1JUNkIiLCAiSUwxUjIiLCAiV05UMyIsCiAgICAgICAgICAiSUZJMjciLCAiQ1hDTDE0IiwgIklHRkJQMyIsICJLUlQxNSIsICJDRDIwMCIpCgpwbG90X2xpc3QgPSBsYXBwbHkoYygxOmxlbmd0aChnZW5lcykpLCBGVU4gPSBmdW5jdGlvbihnZW5lX2lkKSB7CiAgZ2VuZSA9IGdlbmVzW1tnZW5lX2lkXV0KICAKICBzb2JqX2libG9ycyRteV9nZW5lID0gU2V1cmF0OjpGZXRjaERhdGEoc29ial9pYmxvcnMsIGdlbmUpWywgMV0gJT4lCiAgICBhcXVhcml1czo6cnVuX3Jlc2NhbGUoLiwgbmV3X21pbiA9IDAsIG5ld19tYXggPSAxMCkKICAKICBTZXVyYXQ6OkZlYXR1cmVQbG90KHNvYmpfaWJsb3JzLCBmZWF0dXJlcyA9ICJteV9nZW5lIiwgcmVkdWN0aW9uID0gbmFtZTJELCBwdC5zaXplID0gMC4yNSkgKwogICAgZ2dwbG90Mjo6c2NhbGVfY29sb3JfZ3JhZGllbnRuKGNvbG9ycyA9IGFxdWFyaXVzOjpjb2xvcl9nZW5lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IHNlcSgwLCAxMCwgYnkgPSAyLjUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIm1pbiIsIHJlcCgiIiwgMyksICJtYXgiKSkgKwogICAgZ2dwbG90Mjo6bGFicyh0aXRsZSA9IGdlbmUpICsKICAgIGdncGxvdDI6OnRoZW1lKGFzcGVjdC5yYXRpbyA9IDEsCiAgICAgICAgICAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBzaXplID0gMTcpLAogICAgICAgICAgICAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgc2l6ZSA9IDE1KSwKICAgICAgICAgICAgICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNSksCiAgICAgICAgICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsKICAgIFNldXJhdDo6Tm9BeGVzKCkKfSkKCnBsb3RfbGlzdApgYGAKCiMgUiBTZXNzaW9uCgpgYGB7ciBzZXNzaW9uaW5mbywgZWNobyA9IEZBTFNFLCBmb2xkX291dHB1dCA9IFRSVUV9CnNlc3Npb25JbmZvKCkKYGBgCgo=